#include <glad/glad.h>
#define GLFW_INCLUDE_NONE       //这个的作用是防止头文件冲突 定义了 glfw3.h就不一定要放在glad.h后面
#include <GLFW/glfw3.h>

//#define GLFW_INCLUDE_NONE       //这个的作用是防止头文件冲突 定义了 glfw3.h就不一定要放在glad.h后面
//#include <GLFW/glfw3.h>
//#include <glad/glad.h>

//#include "linmath.h"

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <mutex>
#include <math.h>
#include <time.h>

#include "opengl_gp_lib.h"
#include "shadercodetext.h"

#define M_PI (3.14159265358979323846)
#define M_PI_F (3.14159265358979323846f)

//本库中初始化编译的着色器程序代码源指针
const char * const programCodeText[][3]={
    {commonVertShaderCode,cvtUItoFloatFragShaderCode,nullptr},
    {commonVertShaderCode,cvtFloatToUIFragShaderCode,nullptr},
    {commonVertShaderCode,textureCopyShaderCode,nullptr},
    {commonVertShaderCode,textureCopySrcShaderCode,nullptr},
    {commonVertShaderCode,splitTextureUI,nullptr},
    {commonVertShaderCode,splitTextureINT,nullptr},
    {commonVertShaderCode,splitTextureFloat,nullptr},
    {commonVertShaderCode,mergeTexturesUI,nullptr},
    {commonVertShaderCode,mergeTexturesINT,nullptr},
    {commonVertShaderCode,mergeTexturesFloat,nullptr},
    {commonVertShaderCode,textureThresholdUI,nullptr},
    {commonVertShaderCode,textureThresholdINT,nullptr},
    {commonVertShaderCode,textureThresholdFloat,nullptr},
    {commonVertShaderCode,textureRegionOfInterest,nullptr},
    {commonVertShaderCode,texelMath,nullptr},
    {commonVertShaderCode,texturesMath,nullptr},
    {commonVertShaderCode,resizeTextureStr,nullptr},
    {textureTransformVertShader,textureTransformFragShader,nullptr},
    {commonVertShaderCode,setFFTInputTexShaderStr,nullptr},
    {commonVertShaderCode,getFFTResultTexShaderStr,nullptr},
    {nullptr,nullptr,compute2DFFTTexShaderStr},
    {nullptr,nullptr,nullptr},
    {commonVertShaderCode,textureTransposeShaderCode,nullptr},
    {commonVertShaderCode,oneDimHistCalcFShaderCode,nullptr},
    {commonVertShaderCode,tex2DConv2DFShaderCode,nullptr},
    {commonVertShaderCode,tex2DConv2DSobelFShaderCode,nullptr},
    {nullptr,nullptr,tex2DConv2DSobelCShaderCode},

};

enum _ShaderProgramIndex{
    _SPI_CvtUItoFloat,
    _SPI_CvtFloatToUI,
    _SPI_TextureCopy,
    _SPI_TextureCopySrc,
    _SPI_SplitTextureUI,
    _SPI_SplitTextureINT,
    _SPI_SplitTextureFloat,
    _SPI_MergeTexturesUI,
    _SPI_MergeTexturesINT,
    _SPI_MergeTexturesFloat,
    _SPI_TexturesThresholdUI,
    _SPI_TexturesThresholdINT,
    _SPI_TexturesThresholdFloat,
    _SPI_TexturesROI,
    _SPI_TexelMath,
    _SPI_TexturesMath,
    _SPI_ResizeTextures,
    _SPI_TextureTransform,
    _SPI_Set2DFFTInputTex,
    _SPI_Get2DFFTResTex,
    _SPI_2DTexFFT,
    _SPI_Custom2DTexFFT,
    _SPI_TexTranspose,
    _SPI_Tex2DHist1DCalc,
    _SPI_Tex2DConvolve2D,
    _SPI_Tex2DConv2DSobel,
    _SPI_Tex2DCannySobelStep,

    _SPI_Count
};

typedef struct{
    GLFWwindow* window = nullptr;
    std::mutex mutex;
    GLuint fbo = 0;
    GLuint square_vao = 0;  //-1~1范围的方形顶点vao
    GLuint square_vbo = 0;  //-1~1范围的方形顶点vbo
    GLuint programs[_ShaderProgramIndex::_SPI_Count] = {0};
    unsigned int byteMirrorLut = 0; //用于字节镜像的lut 纹理
}GLContextSZ;

static int IsLibInitialed = 0;
static int MaxGLContext = 1;
static GLContextSZ * GLContextArray = nullptr;


//////////
typedef struct{
    int width =0 ;
    int height = 0;
    int colbitcnt = 0;  //列方向的二进制位数 2^N = width
    int rowbitcnt = 0;  //位方向的二进制位数 2^N = height
    unsigned int complexBufs;    //两个用于计算的纹理缓冲区,GL_R32F,由于bingimage，只能使用单通道, 使用纹理数组
    unsigned int wkBuf[2];         //列和行方向的正交因子e^{-j2kpi/N},GL_RG32F
    int hasFFTWINBuf[2] = {0};        //是否已经写入了自定义的窗数据
    unsigned int fftWINBuf[2];      //列和行方向的窗函数缓冲,GL_RG32F
} GLGP_FFTPlan2D_SZ;
//////////

void glgpTest(unsigned int program,int width,int height, void *misc)
{

}


////将4x4的矩阵进行转置
//void _transposeMatrix4x4(float *mat){
//    float (* mat4x4)[4][4] = reinterpret_cast<float(*)[4][4]>(mat);
//    float temp;
//    for (int c = 0; c < 4; ++c) {
//        int r = 0;
//        for (; r < 4; ++r) {
//            if(c == r) break;
//            temp = (*mat4x4)[r][c];
//            (*mat4x4)[r][c] = (*mat4x4)[c][r];
//            (*mat4x4)[c][r] = temp;
//        }
//        if(c == r) continue;
//    }
//}

////将两个 4x4的矩阵进行相乘(不是元素相乘),结果放置到result中,result = mat1 * mat2
////注意 这里的矩阵是内存报局是列优先的,与opengl默认的一致
//void _matrix4x4Mult(float *mat1,float *mat2,float *result){
//    float temp[4][4] = {{0}};
//    float (* amat4x4)[4][4] = reinterpret_cast<float(*)[4][4]>(mat1);
//    float (* bmat4x4)[4][4] = reinterpret_cast<float(*)[4][4]>(mat2);
//    for (int r = 0; r < 4; ++r) {
//        for (int c = 0; c < 4; ++c) {
//            float sum = 0;
//            for (int i = 0; i < 4; ++i) {
//                sum = sum + (*amat4x4)[i][r] * (*bmat4x4)[c][i];
//            }
//            temp[c][r] = sum;
//        }
//    }

//    bmat4x4 = reinterpret_cast<float(*)[4][4]>(result);
//    for (int r = 0; r < 4; ++r) {
//        for (int c = 0; c < 4; ++c) {
//            (*bmat4x4)[c][r] = temp[c][r];
//        }
//    }
//}

///
/// \brief _GaussEliminationSolveMat  使用高斯消去法求解线性方程
/// \param mat  需要求解的矩阵(行列式) ，行优选矩阵 mat[row][col]
/// \param rows 矩阵的行数
/// \param cols 矩阵的列数
/// \param resalut  返回求出的解
/// \return 成功求出解 返回0，否则非零并返回resalut单位矩阵
///
///
int _GaussEliminationSolveMat(float *mat,const int rows,const int cols,float *resalut){
    if(rows<2 || cols < 3 || (cols-rows)!=1 || !mat || !resalut) return -1;

    const float zerothresh = 1e-28f;
    // 高斯消去法 生成上三角行列式
    for (int cyc = 0; cyc < rows-1; ++cyc) {  //最外层行循环
        int  nonezerorow = cyc;
        bool hasnonezerorow = false;
        for (int nonezeroi = cyc; nonezeroi < rows; ++nonezeroi) {  //寻找列头非零的行
            if(mat[nonezeroi*cols + cyc]>=zerothresh || mat[nonezeroi*cols + cyc]<=-zerothresh){
                hasnonezerorow = true;
                if(nonezerorow != nonezeroi){   //把列头非零行交换到参考行
                    float t;
                    for (int j = 0; j < cols; ++j) {
                        t = mat[nonezerorow * cols + j];
                        mat[nonezerorow * cols + j] = mat[nonezeroi*cols + j];
                        mat[nonezeroi*cols + j] = t;
                    }

                }
                break;
            }
        }

        if(!hasnonezerorow) continue;//return -1;

        for (int subrow = cyc + 1; subrow < rows; ++subrow) {   //子行循环
            if(mat[subrow*cols + cyc]>-zerothresh && mat[subrow*cols + cyc]<zerothresh) continue; //本来是零就不要计算了
            double a = static_cast<double>(mat[subrow*cols + cyc]) / static_cast<double>(mat[cyc * cols + cyc]);  //计算行头比列
            for (int col = cyc; col < cols; ++col) {    //执行减法
                mat[subrow * cols + col] = static_cast<float>(
                            static_cast<double>(mat[subrow * cols + col])
                        - a * static_cast<double>(mat[cyc * cols + col])
                        );
            }
        }
    }

    //求取行列式的解
    bool hassolve = true;      //是否存在唯一解
    for (int cyc = rows-1; cyc >= 0; --cyc) {
        double sum = 0;
        int col = cyc + 1;
        for (; col < cols-1; ++col) {
            sum = sum + static_cast<double>(resalut[col] * mat[cyc * cols + col]);
        }
        sum = static_cast<double>(mat[cyc * cols + cols - 1]) - sum;
        sum = sum / static_cast<double>(mat[cyc * cols + cyc]);
        hassolve = hassolve && !std::isnan(sum) && std::isfinite(sum);
        if(!hassolve) break;
        resalut[cyc] = static_cast<float>(sum);
    }

    if(!hassolve) return -1;

    return 0;
}

///
/// \brief _calcInvertedMatrix 高斯消去法从源矩阵计算出其逆矩阵,放置到结果中
/// \param srcmat       源矩阵指针，由调用方申请和释放内存,列优选矩阵 mat[col][row]
/// \param resalut   结果矩阵指针，由调用方申请和释放内存,列优选矩阵 mat[col][row]
/// \param matsize  矩阵的大小，(宽高相等)
/// \return 成功求出逆矩阵 返回0，否则非零并返回resalut单位矩阵
///
int _calcInvertedMatrix(float *srcmat,float *resalut,int matsize){
    if(matsize<2 || !resalut || !srcmat) return -1;
    int &rows = matsize, &cols = matsize;
    const float zerothresh = 1e-28f;

    float *mat = new float[static_cast<size_t>(matsize*matsize)];   //行优选缓冲区
    float *matresbuf = new float[static_cast<size_t>(matsize*matsize)]; //行优选缓冲区
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            mat[i * cols + j] = srcmat[ j*rows + i];
            matresbuf[i * cols + j] = (i==j) * 1.0f;
        }
    }

//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< mat[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }

    // 高斯消去法 生成上三角行列式
    for (int cyc = 0; cyc < rows-1; ++cyc) {  //最外层行循环
        int  nonezerorow = cyc;
        bool hasnonezerorow = false;
        for (int nonezeroi = cyc; nonezeroi < rows; ++nonezeroi) {  //寻找列头非零的行
            if(mat[nonezeroi*cols + cyc]>=zerothresh || mat[nonezeroi*cols + cyc]<=-zerothresh){
                hasnonezerorow = true;
                if(nonezerorow != nonezeroi){   //把列头非零行交换到参考行
                    float t;
                    for (int j = 0; j < cols; ++j) {
                        t = mat[nonezerorow * cols + j];
                        mat[nonezerorow * cols + j] = mat[nonezeroi*cols + j];
                        mat[nonezeroi*cols + j] = t;

                        t = matresbuf[nonezerorow * cols + j];
                        matresbuf[nonezerorow * cols + j] = matresbuf[nonezeroi*cols + j];
                        matresbuf[nonezeroi*cols + j] = t;
                    }

                }
                break;
            }
        }

        if(!hasnonezerorow) continue;//return -1;

        for (int subrow = cyc + 1; subrow < rows; ++subrow) {   //子行循环
            if(mat[subrow*cols + cyc]>-zerothresh && mat[subrow*cols + cyc]<zerothresh) continue; //本来是零就不要计算了
            double a = static_cast<double>(mat[subrow*cols + cyc]) / static_cast<double>(mat[cyc * cols + cyc]);  //计算列头比列
            for (int col = 0; col < cols; ++col) {    //执行减法
                mat[subrow * cols + col] = static_cast<float>(
                            static_cast<double>(mat[subrow * cols + col])
                        - a * static_cast<double>(mat[cyc * cols + col])
                        );

                matresbuf[subrow * cols + col] = static_cast<float>(
                            static_cast<double>(matresbuf[subrow * cols + col])
                        - a * static_cast<double>(matresbuf[cyc * cols + col])
                        );
            }
        }
    }

//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< mat[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }
//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< matresbuf[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }


    // 消去上面三角形的非零值
    bool hassolve = true;      //是否存在唯一解
    for (int cyc = rows-1; cyc >= 0; --cyc) {  //最外层行循环
        hassolve = hassolve && !(mat[cyc*cols + cyc]>-zerothresh && mat[cyc*cols + cyc]<zerothresh);    //对角线上的值为零则无解
        if(!hassolve) break;

        for (int subrow = cyc - 1; subrow >= 0; --subrow) {   //子行循环
            if(mat[subrow*cols + cyc]>-zerothresh && mat[subrow*cols + cyc]<zerothresh) continue; //本来是零就不要计算了
            double a = static_cast<double>(mat[subrow*cols + cyc]) / static_cast<double>(mat[cyc * cols + cyc]);  //计算列尾比列
            for (int col = 0; col < cols; ++col) {    //执行减法
                mat[subrow * cols + col] = static_cast<float>(
                            static_cast<double>(mat[subrow * cols + col])
                        - a * static_cast<double>(mat[cyc * cols + col])
                        );

                matresbuf[subrow * cols + col] = static_cast<float>(
                            static_cast<double>(matresbuf[subrow * cols + col])
                        - a * static_cast<double>(matresbuf[cyc * cols + col])
                        );
            }
        }
    }

//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< mat[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }
//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< matresbuf[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }

    if(hassolve){
        for (int row = 0; row < rows; ++row) {  //最外层行循环
             hassolve = hassolve && !(mat[row*cols + row]>-zerothresh && mat[row*cols + row]<zerothresh);    //对角线上的值为零则无解
             if(!hassolve) break;

             double a = static_cast<double>(mat[row*cols + row]) ;
             for (int col = 0; col < cols; ++col) {
                 matresbuf[row * cols + col] = static_cast<float>(
                             static_cast<double>(matresbuf[row * cols + col]) / a
                         );
             }

        }
    }

//    std::cout<<"------------------------------"<<std::endl;
//    for (int i = 0; i < rows; ++i) {
//        for (int j = 0; j < cols; ++j) {
//            std::cout<< matresbuf[i * cols + j] <<", ";
//        }
//        std::cout<<std::endl;
//    }

    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            if(hassolve){                   //有解就转置放入到结果内存
                resalut[j * rows + i] = matresbuf[i * cols + j];
            }else{
                resalut[j * rows + i] = (i==j) * 1.0f;  //没有解,写入单位矩阵
            }
        }
    }

    delete [] mat;
    delete [] matresbuf;

    if(!hassolve) return -1;
    return 0;
}

int glgpGetMaxGLContext()
{
    return MaxGLContext;
}

void glgpSetMaxGLContext(int maxCtx)
{
    if(IsLibInitialed) return;
    if(maxCtx<1) maxCtx = 1;
    MaxGLContext = maxCtx;
}

int glgpInitContext()
{
    int res = 0;

    if(IsLibInitialed) return res;
    if(!glfwInit()){
        res = 1;
    }

    IsLibInitialed = 1;
    if(GLContextArray){
        for (int i = 0; i < MaxGLContext; ++i) {
            GLContextArray[i].mutex.lock();
            glfwDestroyWindow(GLContextArray[i].window);
            GLContextArray[i].mutex.unlock();
        }
        delete [] GLContextArray;
        GLContextArray = nullptr;
    }
    GLContextArray = new GLContextSZ[static_cast<size_t>(MaxGLContext)];

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 这里设置opengl 的版本
    glfwWindowHint(GLFW_CLIENT_API ,GLFW_OPENGL_ES_API);
//    glfwWindowHint(GLFW_CLIENT_API ,GLFW_OPENGL_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    //    glfwWindowHint(GLFW_RED_BITS, 32);
    //    glfwWindowHint(GLFW_GREEN_BITS, 32);
    //    glfwWindowHint(GLFW_BLUE_BITS, 32);
    //    glfwWindowHint(GLFW_ALPHA_BITS, 32);
//    glfwWindowHint(GLFW_SAMPLES, 4); //启用多重采样
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    for (int i = 0; i < MaxGLContext; ++i) {
        GLContextArray[i].window =
                glfwCreateWindow(1, 1, "", nullptr, nullptr);
    }

    GLContextArray[0].mutex.lock();

    glfwMakeContextCurrent(GLContextArray[0].window);
    //加载gl函数
    if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
        fprintf(stderr, "Failed to initialize GLAD");
        return -1;
    }
//    if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
//        fprintf(stderr, "Failed to initialize GLAD");
//        return -1;
//    }

    std::cout<<glGetString(GL_VERSION)<<std::endl;

    glfwMakeContextCurrent(nullptr);
    GLContextArray[0].mutex.unlock();


    for (int i = 0; i < MaxGLContext; ++i) {
        GLContextArray[i].mutex.lock();

        if(GLContextArray[i].window){
            glfwSetWindowUserPointer(GLContextArray[i].window,reinterpret_cast<void *>(&GLContextArray[i])); //保结构指针 让线程中查询
        }

        glfwMakeContextCurrent(GLContextArray[i].window);
        glGenFramebuffers(1,&GLContextArray[i].fbo);

        //加载通用-1~1方形顶点数据到 vao
        glGenVertexArrays(1,&GLContextArray[i].square_vao);
        glBindVertexArray(GLContextArray[i].square_vao);
        glGenBuffers(1, &GLContextArray[i].square_vbo);
        glBindBuffer(GL_ARRAY_BUFFER, GLContextArray[i].square_vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(square_verts), square_verts, GL_STATIC_DRAW);

        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
                              3 * sizeof(square_verts[0]), nullptr);
        glBindVertexArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        //新建字节镜像lut 纹理
        glGenTextures(1, &GLContextArray[i].byteMirrorLut);
        glBindTexture(GL_TEXTURE_2D, GLContextArray[i].byteMirrorLut);
        glTexStorage2D(GL_TEXTURE_2D, 1 ,GL_R32I , 256,1);
        glTexSubImage2D(GL_TEXTURE_2D,0,
                        0,0,256,1,
                        GL_RED_INTEGER, GL_INT,mirrorbytes
                        );
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glBindTexture(GL_TEXTURE_2D, 0);

        // 编译链接库需要用到的着色器程序
        for (int j = 0; j < _ShaderProgramIndex::_SPI_Count; ++j) {
            GLContextArray[i].programs[j] =
                    glgpComplieShaderProgram3(programCodeText[j][0],programCodeText[j][1],programCodeText[j][2]);
        }

        glfwMakeContextCurrent(nullptr);

        GLContextArray[i].mutex.unlock();
    }

    return res;
}

int glgpReleaseContext()
{
    glfwTerminate();
    IsLibInitialed = 0;
    return 0;
}

void glgpPollGlfwEvents()
{
    glfwPollEvents();
}

void glgpLockGLContext(int ctx_index)
{
    if(!IsLibInitialed) return;
    if(!GLContextArray) return;
    if(ctx_index<0 || ctx_index>=MaxGLContext) return;

    GLContextArray[ctx_index].mutex.lock();
    glfwMakeContextCurrent(GLContextArray[ctx_index].window);
}

int glgpTrylockGLContext(int ctx_index)
{
    if(!IsLibInitialed) return 0;
    if(!GLContextArray) return 0;
    if(ctx_index<0 || ctx_index>=MaxGLContext) return 0;

    if(GLContextArray[ctx_index].mutex.try_lock()){
        glfwMakeContextCurrent(GLContextArray[ctx_index].window);
        return 1;
    }
    return 0;
}

void glgpUnlockGLContext(int ctx_index)
{
    if(!IsLibInitialed) return;
    if(!GLContextArray) return;
    if(ctx_index<0 || ctx_index>=MaxGLContext) return;

    glfwMakeContextCurrent(nullptr);
    GLContextArray[ctx_index].mutex.unlock();
}



unsigned int glgpComplieShaderProgram3(const char * const vertcode, const char * const fragcode, const char * const computecode)
{
    GLuint vertex_shader = 0;
    GLuint fragment_shader = 0;
    GLuint compute_shader = 0;
    GLuint program = 0;

    int  success;
    int infoLogLen = 4*1024;
    char *infoLog = new char[static_cast<size_t>(infoLogLen)];

    if(vertcode){
        vertex_shader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex_shader, 1, &vertcode, nullptr);
        glCompileShader(vertex_shader);
        success = 0;
        infoLog[0] = 0;
        glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetShaderInfoLog(vertex_shader, infoLogLen, nullptr, infoLog);
            std::cerr << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
        }
    }

    if(fragcode){
        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment_shader, 1, &fragcode, nullptr);
        glCompileShader(fragment_shader);

        success = 0;
        glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetShaderInfoLog(fragment_shader, infoLogLen, nullptr, infoLog);
            std::cerr << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog;
        }
    }

    if(computecode){
        compute_shader = glCreateShader(GL_COMPUTE_SHADER);	// GL_COMPUTE_SHADER 类型为计算着色器
        glShaderSource(compute_shader , 1 , &computecode , nullptr);	//
        glCompileShader(compute_shader) ;		//编译
        success = 0;
        glGetShaderiv(compute_shader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetShaderInfoLog(compute_shader, infoLogLen, nullptr, infoLog);
            std::cerr << "ERROR::SHADER::COMPUTE::COMPILATION_FAILED\n" << infoLog;
        }
    }

    if(vertex_shader || fragment_shader || compute_shader){
        program = glCreateProgram();
        if(vertex_shader) glAttachShader(program, vertex_shader);
        if(fragment_shader) glAttachShader(program, fragment_shader);
        if(compute_shader) glAttachShader(program, compute_shader);

        glLinkProgram(program);
        success = 0;
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetProgramInfoLog(program, infoLogLen, nullptr, infoLog);
            std::cerr << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog;
        }

        //编译完就没有用了 删除吧
        if(vertex_shader) glDeleteShader(vertex_shader);
        if(fragment_shader) glDeleteShader(fragment_shader);
        if(fragment_shader) glDeleteShader(compute_shader);
    }

    delete [] infoLog;

    return program;
}

void glgpSetUniformByLoc(unsigned int program, int location, char type, int arraysize, int len, const void *data)
{
    if(len < 1 || program < 1 || !data) return;

    glUseProgram(program);
    switch (type) {
    case 'i':
    case 'I':
        if(len==1){
            glUniform1iv(location, arraysize ,reinterpret_cast<const GLint*>(data));
        }else if(len==2){
            glUniform2iv(location,arraysize ,reinterpret_cast<const GLint*>(data));
        }else if(len==3){
            glUniform3iv(location,arraysize ,reinterpret_cast<const GLint*>(data));
        }else if(len==4){
            glUniform4iv(location,arraysize ,reinterpret_cast<const GLint*>(data));
        }
        break;
    case 'u':
    case 'U':
        if(len==1){
            glUniform1uiv(location,arraysize , reinterpret_cast<const GLuint*>(data));
        }else if(len==2){
            glUniform2uiv(location,arraysize ,reinterpret_cast<const GLuint*>(data));
        }else if(len==3){
            glUniform3uiv(location,arraysize ,reinterpret_cast<const GLuint*>(data));
        }else if(len==4){
            glUniform4uiv(location,arraysize ,reinterpret_cast<const GLuint*>(data));
        }
        break;
    case 'f':
    case 'F':
        if(len==1){
            glUniform1fv(location,arraysize ,reinterpret_cast<const GLfloat*>(data));
        }else if(len==2){
            glUniform2fv(location,arraysize ,reinterpret_cast<const GLfloat*>(data));
        }else if(len==3){
            glUniform3fv(location,arraysize ,reinterpret_cast<const GLfloat*>(data));
        }else if(len==4){
            glUniform4fv(location,arraysize ,reinterpret_cast<const GLfloat*>(data));
        }
        break;
    case 'd':
    case 'D':
        if(len==1){
            glUniform1dv(location, arraysize ,reinterpret_cast<const GLdouble*>(data));
        }else if(len==2){
            glUniform2dv(location,arraysize ,reinterpret_cast<const GLdouble*>(data));
        }else if(len==3){
            glUniform3dv(location,arraysize ,reinterpret_cast<const GLdouble*>(data));
        }else if(len==4){
            glUniform4dv(location,arraysize ,reinterpret_cast<const GLdouble*>(data));
        }
        break;
    case 'm':
    case 'M':
        glUniformMatrix4fv(location,arraysize,GL_FALSE,reinterpret_cast<const GLfloat*>(data));
        break;
    default:
        break;
    }

    glUseProgram(0);
}


void glgpSetUniform(unsigned int program,const char  * const uniformname,
                    char type,int arraysize, int len, const void *data)
{
    int loc = glGetUniformLocation(program,uniformname);
    if(len < 1 || program < 1 || !data) return;
    glgpSetUniformByLoc(program,loc,type,arraysize,len,data);
}

unsigned int glgpAllocateGLTexture2D(GLGPFromatEnum format, int width, int height,void * initdata)
{
    unsigned int tex = 0;
    GLenum texf = GL_RGBA32F;

    if(width<1 || height<1) return tex;

    GLuint dstcomponent = GL_RED_INTEGER;
    GLuint dstformat = GL_UNSIGNED_BYTE;
    switch (format) {
    case GLGPFromatEnum::GLF_R8UI:
        texf = GL_R8UI; //相当于 glTexStorage2D的 GL_RGBA_INTEGER + GL_UNSIGNED_BYTE
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GLGPFromatEnum::GLF_R16UI:
        texf = GL_R16UI;
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GLGPFromatEnum::GLF_R32UI:
        texf = GL_R32UI;
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GLGPFromatEnum::GLF_R8I:
        texf = GL_R8I;
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GLGPFromatEnum::GLF_R16I:
        texf = GL_R16I;
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GLGPFromatEnum::GLF_R32I:
        texf = GL_R32I;
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_INT;
        break;
    case GLGPFromatEnum::GLF_R32F:
        texf = GL_R32F;
        dstcomponent = GL_RED;
        dstformat = GL_FLOAT;
        break;
    case GLGPFromatEnum::GLF_RGB8UI:
        texf = GL_RGB8UI;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GLGPFromatEnum::GLF_RGB16UI:
        texf = GL_RGB16UI;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GLGPFromatEnum::GLF_RGB32UI:
        texf = GL_RGB32UI;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GLGPFromatEnum::GLF_RGB8I:
        texf = GL_RGB8I;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GLGPFromatEnum::GLF_RGB16I:
        texf = GL_RGB16I;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GLGPFromatEnum::GLF_RGB32I:
        texf = GL_RGB32I;
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_INT;
        break;
    case GLGPFromatEnum::GLF_RGB32F:
        texf = GL_RGB32F;
        dstcomponent = GL_RGB;
        dstformat = GL_FLOAT;
        break;
    case GLGPFromatEnum::GLF_RGBA8UI:
        texf = GL_RGBA8UI;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GLGPFromatEnum::GLF_RGBA16UI:
        texf = GL_RGBA16UI;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GLGPFromatEnum::GLF_RGBA32UI:
        texf = GL_RGBA32UI;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GLGPFromatEnum::GLF_RGBA8I:
        texf = GL_RGBA8I;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GLGPFromatEnum::GLF_RGBA16I:
        texf = GL_RGBA16I;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GLGPFromatEnum::GLF_RGBA32I:
        texf = GL_RGBA32I;
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_INT;
        break;
    case GLGPFromatEnum::GLF_RG32F:
        texf = GL_RG32F;
        dstcomponent = GL_RG;
        dstformat = GL_FLOAT;
        break;
    case GLGPFromatEnum::GLF_RGBA32F:
        texf = GL_RGBA32F;
        dstcomponent = GL_RGBA;
        dstformat = GL_FLOAT;
        break;
        //    default:
        //        texf = GL_RGBA32F;
        //        break;
    }

    //为纹理生成一个新的名称
    glGenTextures(1 , &tex);
    //绑定到规则的2D 纹理目标， 即创建纹理
    glBindTexture(GL_TEXTURE_2D, tex);
    // 为纹理分配不可变的存储空间

//    glTexImage2D(GL_TEXTURE_2D,0,static_cast<GLint>(texf),width,height,0,dstcomponent,dstformat,nullptr);   // glTexImage2D创建的纹理是可变的，不能用于image load/store，所以不使用它创建纹理了
    glTexStorage2D(GL_TEXTURE_2D, 1 , texf , width , height );  // glTexStorage2D一般用于创建不可变纹理,不可变纹理才能绑定到image load/store单元
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);      //GL_NEAREST
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);       //GL_LINEAR wiki上说只支持浮点数类型的纹理自动插值
    float borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };                     //新的opengl版本已经弃用了纹理边界这个属性,但这里还是设置一下吧
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,borderColor);

    if(initdata){
        //        //glTexSubImage2D format type 参数详细要参考 glTexStorage2D帮助文档，里面有个表格
        //        // GL_RGBA8UI 对应 GL_RGBA_INTEGER + GL_UNSIGNED_BYTE
        //        //  GL_RGBA8 对应GL_RGBA + GL_UNSIGNED_BYTE
        //        //https://www.khronos.org/registry/OpenGL-Refpages/es3/
        glTexSubImage2D(GL_TEXTURE_2D,0,
                        0,0,
                        width,height,
                        dstcomponent,dstformat,
                        initdata
                        );
        GLenum err = glGetError();
        if(err){
            std::cerr<<"glgpAllocateGLTexture2D error,maybe cause by glTexSubImage2D. error code = "<< err << std::endl;
        }
    }

    //解除 纹理目标的绑定
    glBindTexture(GL_TEXTURE_2D, 0);

    return tex;
}

void glgpGetTexture2DInfo(unsigned int tex, int *width, int *height, int *numComponents, int *dbytesize, char *dtype)
{
    if(!tex) return;

    int w=0,h=0,f=0,t=0,bits=0;

    glBindTexture(GL_TEXTURE_2D,tex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&w);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&h);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&f);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&t);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_SIZE,&bits);
    glBindTexture(GL_TEXTURE_2D,0);

    if(width) *width = w;
    if(height) *height = h;

    if(numComponents){
        switch (f) {
        case GL_R8I:
        case GL_R16I:
        case GL_R32I:
        case GL_R8UI:
        case GL_R16UI:
        case GL_R32UI:
        case GL_R8:
        case GL_R16:
        case GL_R32F:
            *numComponents = 1;
            break;
        case GL_RG8I:
        case GL_RG16I:
        case GL_RG32I:
        case GL_RG8UI:
        case GL_RG16UI:
        case GL_RG32UI:
        case GL_RG8:
        case GL_RG16:
        case GL_RG32F:
            *numComponents = 2;
            break;
        case GL_RGB8I:
        case GL_RGB16I:
        case GL_RGB32I:
        case GL_RGB8UI:
        case GL_RGB16UI:
        case GL_RGB32UI:
        case GL_RGB8:
        case GL_RGB16:
        case GL_RGB32F:
            *numComponents = 3;
            break;
        case GL_RGBA8I:
        case GL_RGBA16I:
        case GL_RGBA32I:
        case GL_RGBA8UI:
        case GL_RGBA16UI:
        case GL_RGBA32UI:
        case GL_RGBA8:
        case GL_RGBA16:
        case GL_RGBA32F:
            *numComponents = 4;
            break;
        default:
            break;
        }
    }

    if(dtype){
        if(t == GL_FLOAT){
            dtype[0] = 'f';
        }else if(t == GL_UNSIGNED_INT ){
            dtype[0] = 'u';
        }else if(t ==  GL_INT){
            dtype[0] = 'i';
        }
    }

    if(dbytesize){
        *dbytesize = (bits+7) / 8;
    }
}



int glgpSetTexImage2D(unsigned int tex, void *data)    // GLFromatEnum dataformat, int width, int height,
{
    if(tex && data){    // && width>1 && height >1
        GLint dataformat=GL_R8UI,width=0,height=0;
        glBindTexture(GL_TEXTURE_2D,tex);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&dataformat);
        //        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&t);
        //        glBindTexture(GL_TEXTURE_2D,0);

        GLuint dstcomponent = GL_RED_INTEGER;
        GLuint dstformat = GL_UNSIGNED_BYTE;
        switch (dataformat) {
        case GL_R8UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_R16UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_R32UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_R8I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_R16I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_R32I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_R32F:
            dstcomponent = GL_RED;
            dstformat = GL_FLOAT;
            break;
        case GL_RGB8UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_RGB16UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_RGB32UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_RGB8I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_RGB16I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_RGB32I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_RGB32F:
            dstcomponent = GL_RGB;
            dstformat = GL_FLOAT;
            break;
        case GL_RGBA8UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_RGBA16UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_RGBA32UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_RGBA8I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_RGBA16I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_RGBA32I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_RG32F:
            dstcomponent = GL_RG;
            dstformat = GL_FLOAT;
            break;
        case GL_RGBA32F:
            dstcomponent = GL_RGBA;
            dstformat = GL_FLOAT;
            break;
            //    default:
            //        texf = GL_RGBA32F;
            //        break;
        }

        //        glBindTexture(GL_TEXTURE_2D, tex);
        glTexSubImage2D(GL_TEXTURE_2D,0,
                        0,0,
                        width,height,
                        dstcomponent,dstformat,
                        data
                        );

        GLenum err = glGetError();
        if(err){
            std::cerr<<"glgpSetTexImage2D error,maybe cause by glTexSubImage2D. error code = "<< err << std::endl;
        }

        glBindTexture(GL_TEXTURE_2D, 0);
        return static_cast<int>(err);
    }

    return -1;
}

int glgpGetTexImage2D(unsigned int tex,void *data) //GLFromatEnum dataformat,int width,int height,
{
    if(tex){
        GLint dataformat=GL_R8UI,width=0,height=0;
        glBindTexture(GL_TEXTURE_2D,tex);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&dataformat);
        //        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&t);
        glBindTexture(GL_TEXTURE_2D,0);

        GLuint dstcomponent = GL_RED_INTEGER;
        GLuint dstformat = GL_UNSIGNED_BYTE;
        switch (dataformat) {
        case GL_R8UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_R16UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_R32UI:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_R8I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_R16I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_R32I:
            dstcomponent = GL_RED_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_R32F:
            dstcomponent = GL_RED;
            dstformat = GL_FLOAT;
            break;
        case GL_RGB8UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_RGB16UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_RGB32UI:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_RGB8I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_RGB16I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_RGB32I:
            dstcomponent = GL_RGB_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_RGB32F:
            dstcomponent = GL_RGB;
            dstformat = GL_FLOAT;
            break;
        case GL_RGBA8UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_BYTE;
            break;
        case GL_RGBA16UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_SHORT;
            break;
        case GL_RGBA32UI:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_UNSIGNED_INT;
            break;
        case GL_RGBA8I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_BYTE;
            break;
        case GL_RGBA16I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_SHORT;
            break;
        case GL_RGBA32I:
            dstcomponent = GL_RGBA_INTEGER;
            dstformat = GL_INT;
            break;
        case GL_RG32F:
            dstcomponent = GL_RG;
            dstformat = GL_FLOAT;
            break;
        case GL_RGBA32F:
            dstcomponent = GL_RGBA;
            dstformat = GL_FLOAT;
            break;
            //    default:
            //        texf = GL_RGBA32F;
            //        break;
        }

        GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                    glfwGetWindowUserPointer(glfwGetCurrentContext())
                    );
        glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
        //        glBindTexture(GL_TEXTURE_2D,tex);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,tex,0);
        //        glBindTexture(GL_TEXTURE_2D,0);
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;    //最少要附加一个颜色缓冲附件
            glBindFramebuffer(GL_FRAMEBUFFER,0);
            return -1;
        }

//                //查询第二种支持的格式类型对
//                GLint eReadType;
//                GLint eReadFormat;
//                glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE,&eReadType);
//                glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT,&eReadFormat);
//                printf("second support (format,type) pair = (0x%04X, 0x%04X) \n",eReadFormat,eReadType);

        //opengl es glReadPixels 最多只支持两对 format,type 对,颜色纹理自身的内部格式对总是支持的(第一种)，
        //查询第二种支持的格式类型对 代码见上面
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        glReadPixels(0, 0,  //offset position
                     width, height,
                     dstcomponent,
                     dstformat, data);

        GLenum err = glGetError();
        if(err){
            std::cerr<<"glgpGetTexImage2D error,maybe cause by glReadPixels. error code = "<< err << std::endl;
        }


        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,0,0);
        glBindFramebuffer(GL_FRAMEBUFFER,0);

        //        glDeleteBuffers(1,&fbo);
        return static_cast<int>(glGetError());
    }
    return -1;
}

void glgpBindImageTexture(unsigned int unit, unsigned int tex)
{
    if(tex){
        GLint texf = GL_RGBA32F;
        GLint status = 0;
        glBindTexture(GL_TEXTURE_2D,tex);
        glGetTexParameteriv(GL_TEXTURE_2D,GL_TEXTURE_IMMUTABLE_FORMAT,&status);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&texf);
        glBindTexture(GL_TEXTURE_2D,0);

        if(!status){        // glBindImageTexture 只能绑定不可变纹理，而用glTexImage生成的纹理并不是不可变纹理
            std::cerr<<"glgpBindImageTexture can not bind texture that is not immutable ! "<< std::endl;
            return;
        }

        //https://www.khronos.org/registry/OpenGL-Refpages/es3.1/
        glBindImageTexture(unit,tex,0,GL_FALSE,0,GL_READ_WRITE,static_cast<GLenum>(texf));

        GLenum err = glGetError();
        if(err){
            std::cerr<<"glgpBindImageTexture error,maybe cause by glBindImageTexture. error code = "<< err << std::endl;
        }
    }else{
        glBindImageTexture(unit,0,0,GL_FALSE,0,GL_READ_ONLY,GL_RGBA32F);
    }

}

void glgpInvokeComputeProgram(unsigned int program,
                              unsigned int workgroup_x,
                              unsigned int workgroup_y,
                              unsigned int workgroup_z)
{
    if(program){
        glUseProgram(program);
        glDispatchCompute(workgroup_x,workgroup_y,workgroup_z);
        glUseProgram(0);
    }
}


void glgpDeleteShaderProgram(unsigned int program)
{
    if(program) glDeleteProgram(program);
}

void glgpDeleteGLTexture(unsigned int tex)
{
    if(tex) glDeleteTextures(1,&tex);
}


void glgpDeleteGLTextures(int len, unsigned int *texs)
{
    if(len > 0 && texs) glDeleteTextures(len,texs);
}


void glgpSetTexSwizzle(unsigned tex, const char *swizzle)
{
    if(!tex || !swizzle) return;

    GLint seq[]={GL_RED,GL_GREEN,GL_BLUE,GL_ALPHA};
    for (int i = 0; i < 4; ++i) {
        switch (swizzle[i]) {
        case 'r':
        case 'R':
            seq[i] = GL_RED;
            break;
        case 'g':
        case 'G':
            seq[i] = GL_GREEN;
            break;
        case 'b':
        case 'B':
            seq[i] = GL_BLUE;
            break;
        case 'a':
        case 'A':
            seq[i] = GL_ALPHA;
            break;
        case '0':
            seq[i] = GL_ZERO;
            break;
        case '1':
            seq[i] = GL_ONE;
            break;
        default:
            std::cerr<<"glgpSetTexSwizzle don't support sizzle '"<<swizzle[i]<<"' !";
            break;
        }
    }

    glBindTexture(GL_TEXTURE_2D,tex);
    glTexParameteriv(GL_TEXTURE_2D,GL_TEXTURE_SWIZZLE_RGBA,seq);
    glBindTexture(GL_TEXTURE_2D,0);

}

unsigned int glgpAllocateGLPixelBufferObj(int type, int usagehint, size_t bytesize)
{
    GLuint res = 0;
    GLenum target = GL_PIXEL_UNPACK_BUFFER;
    GLenum usage = GL_DYNAMIC_DRAW;

    if(bytesize < 1) return res;

    // 绑定为 `GL_PIXEL_UNPACK_BUFFER` 时，
    // `glTexImage2D()` 和 `glTexSubImage2D()` 数据指针为NULL,
    // 表示从 PBO 中解包(unpack)像素数据并复制到gpu(例如纹理)
    //  -------------------------------------------------
    //  绑定为 `GL_PIXEL_PACK_BUFFER` 时，`glReadPixels()` 数据指针为NULL,
    //  表示从帧缓冲区中读取像素数据并打包进(pack) PBO
    if(type != GLGP_PBO_ToOpenGL){
        target = GL_PIXEL_PACK_BUFFER;
    }

    switch (usagehint) {
    case GLGP_BUF_USAGE_STREAM_DRAW:
        usage = GL_STREAM_DRAW;
        break;
    case GLGP_BUF_USAGE_STREAM_READ:
        usage = GL_STREAM_READ;
        break;
    case GLGP_BUF_USAGE_STREAM_COPY:
        usage = GL_STREAM_COPY;
        break;

    case GLGP_BUF_USAGE_STATIC_DRAW:
        usage = GL_STATIC_DRAW;
        break;
    case GLGP_BUF_USAGE_STATIC_READ:
        usage = GL_STATIC_READ;
        break;
    case GLGP_BUF_USAGE_STATIC_COPY:
        usage = GL_STATIC_COPY;
        break;

    case GLGP_BUF_USAGE_DYNAMIC_DRAW:
        usage = GL_DYNAMIC_DRAW;
        break;
    case GLGP_BUF_USAGE_DYNAMIC_READ:
        usage = GL_DYNAMIC_READ;
        break;
    case GLGP_BUF_USAGE_DYNAMIC_COPY:
        usage = GL_DYNAMIC_COPY;
        break;
        //    default:
        //        break;
    }

    glGenBuffers(1, &res);
    glBindBuffer(target,res);
    glBufferData(target, static_cast<GLsizeiptr>(bytesize), nullptr, usage);
    glBindBuffer(target,0);
    return res;
}


void *glgpMapPBOMemory(unsigned int pboid, int type)
{
    void * res = nullptr;
    if(glIsBuffer(pboid) != GL_TRUE){
        std::cerr<<"glgpMapPBOMemory input is not a buffer object!"<<std::endl;
        return res;
    }

    GLenum target = GL_PIXEL_UNPACK_BUFFER;
    if(type != GLGP_PBO_ToOpenGL){
        target = GL_PIXEL_PACK_BUFFER;
    }

    GLint64 size = 0;
    glBindBuffer(target, pboid);
    glGetBufferParameteri64v(target, GL_BUFFER_SIZE, &size);
    res = glMapBufferRange(target, 0, size,
                           GL_MAP_READ_BIT | GL_MAP_WRITE_BIT ); //gl es3 没有glMapBuffer函数
    glBindBuffer(target, 0);
    if(!res){
        std::cerr<<"glgpMapPBOMemory Failed!"<<std::endl;
    }

    GLenum err = glGetError();
    if(err){
        std::cerr<<"opengl error may be cause by glgpMapPBOMemory! error code = "<< err <<std::endl;
    }

    return res;
}

int glgpUnmapPBOMemory(unsigned int pboid,int type){
    int res = -1;
    if(glIsBuffer(pboid) != GL_TRUE){
        std::cerr<<"glgpUnmapPBOMemory input is not a buffer object!"<<std::endl;
        return res;
    }

    GLenum target = GL_PIXEL_UNPACK_BUFFER;
    if(type != GLGP_PBO_ToOpenGL){
        target = GL_PIXEL_PACK_BUFFER;
    }
    glBindBuffer(target, pboid);
    if(glUnmapBuffer(target)){
        res = 0;
    }
    glBindBuffer(target, 0);

    GLenum err = glGetError();
    if(err){
        std::cerr<<"opengl error may be cause by glgpUnmapPBOMemory! error code = "<< err <<std::endl;
    }

    return res;
}

int glgpAsyncCopyPBOtoTex2D(unsigned int pboid, unsigned int dsttex)
{
    if(!pboid && !dsttex) return -1;
    if(glIsBuffer(pboid) != GL_TRUE || glIsTexture(dsttex) != GL_TRUE) return -1;

    int w=0,h=0,f=0,t=0,bits=0;
    GLint64 numComponents=0;
    GLint64 size = 0;

    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&w);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&h);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&f);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&t);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_SIZE,&bits);

    switch (f) {
    case GL_R8I:
    case GL_R16I:
    case GL_R32I:
    case GL_R8UI:
    case GL_R16UI:
    case GL_R32UI:
    case GL_R8:
    case GL_R16:
    case GL_R32F:
        numComponents = 1;
        break;
    case GL_RG8I:
    case GL_RG16I:
    case GL_RG32I:
    case GL_RG8UI:
    case GL_RG16UI:
    case GL_RG32UI:
    case GL_RG8:
    case GL_RG16:
    case GL_RG32F:
        numComponents = 2;
        break;
    case GL_RGB8I:
    case GL_RGB16I:
    case GL_RGB32I:
    case GL_RGB8UI:
    case GL_RGB16UI:
    case GL_RGB32UI:
    case GL_RGB8:
    case GL_RGB16:
    case GL_RGB32F:
        numComponents = 3;
        break;
    case GL_RGBA8I:
    case GL_RGBA16I:
    case GL_RGBA32I:
    case GL_RGBA8UI:
    case GL_RGBA16UI:
    case GL_RGBA32UI:
    case GL_RGBA8:
    case GL_RGBA16:
    case GL_RGBA32F:
        numComponents = 4;
        break;
    default:
        break;
    }

    numComponents = numComponents * w * h * (bits / 8);

    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboid);
    glGetBufferParameteri64v(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &size);

    if(size != numComponents){
        std::cerr<<"warnning:glgpAsyncCopyPBOtoTex2D size of PBO not equal to size of Texture !"<<std::endl;
    }

    GLuint dstcomponent = GL_RED_INTEGER;
    GLuint dstformat = GL_UNSIGNED_BYTE;
    switch (f) {
    case GL_R8UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_R16UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_R32UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_R8I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_R16I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_R32I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_R32F:
        dstcomponent = GL_RED;
        dstformat = GL_FLOAT;
        break;
    case GL_RGB8UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_RGB16UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_RGB32UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_RGB8I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_RGB16I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_RGB32I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_RGB32F:
        dstcomponent = GL_RGB;
        dstformat = GL_FLOAT;
        break;
    case GL_RGBA8UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_RGBA16UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_RGBA32UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_RGBA8I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_RGBA16I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_RGBA32I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_RG32F:
        dstcomponent = GL_RG;
        dstformat = GL_FLOAT;
        break;
    case GL_RGBA32F:
        dstcomponent = GL_RGBA;
        dstformat = GL_FLOAT;
        break;
        //    default:
        //        texf = GL_RGBA32F;
        //        break;
    }

    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0,
                    w,h,
                    dstcomponent,dstformat,
                    nullptr //当GL_PIXEL_UNPACK_BUFFER有绑定时,这个参数是指绑定到GL_PIXEL_UNPACK_BUFFER偏移内容复制到纹理中
                    );

    glBindTexture(GL_TEXTURE_2D,0);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);

    GLenum err = glGetError();
    if(err){
        std::cerr<<"opengl error may be cause by glgpAsyncCopyPBOtoTex2D! error code = "<< err <<std::endl;
    }
    return static_cast<int>(err);
}

int glgpAsyncCopyTex2DtoPBO( unsigned int srctex, unsigned int pboid){
    if(!pboid && !srctex) return -1;
    if(glIsBuffer(pboid) != GL_TRUE || glIsTexture(srctex) != GL_TRUE) return -1;

    int w=0,h=0,f=0,t=0,bits=0;
    GLint64 numComponents=0;
    GLint64 size = 0;

    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&w);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&h);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&f);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&t);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_SIZE,&bits);
    glBindTexture(GL_TEXTURE_2D,0);

    switch (f) {
    case GL_R8I:
    case GL_R16I:
    case GL_R32I:
    case GL_R8UI:
    case GL_R16UI:
    case GL_R32UI:
    case GL_R8:
    case GL_R16:
    case GL_R32F:
        numComponents = 1;
        break;
    case GL_RG8I:
    case GL_RG16I:
    case GL_RG32I:
    case GL_RG8UI:
    case GL_RG16UI:
    case GL_RG32UI:
    case GL_RG8:
    case GL_RG16:
    case GL_RG32F:
        numComponents = 2;
        break;
    case GL_RGB8I:
    case GL_RGB16I:
    case GL_RGB32I:
    case GL_RGB8UI:
    case GL_RGB16UI:
    case GL_RGB32UI:
    case GL_RGB8:
    case GL_RGB16:
    case GL_RGB32F:
        numComponents = 3;
        break;
    case GL_RGBA8I:
    case GL_RGBA16I:
    case GL_RGBA32I:
    case GL_RGBA8UI:
    case GL_RGBA16UI:
    case GL_RGBA32UI:
    case GL_RGBA8:
    case GL_RGBA16:
    case GL_RGBA32F:
        numComponents = 4;
        break;
    default:
        break;
    }

    numComponents = numComponents * w * h * (bits / 8);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, pboid);
    glGetBufferParameteri64v(GL_PIXEL_PACK_BUFFER, GL_BUFFER_SIZE, &size);

    if(size != numComponents){
        std::cerr<<"warnning:glgpAsyncCopyTex2DtoPBO size of PBO not equal to size of Texture !"<<std::endl;
    }

    GLuint dstcomponent = GL_RED_INTEGER;
    GLuint dstformat = GL_UNSIGNED_BYTE;
    switch (f) {
    case GL_R8UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_R16UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_R32UI:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_R8I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_R16I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_R32I:
        dstcomponent = GL_RED_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_R32F:
        dstcomponent = GL_RED;
        dstformat = GL_FLOAT;
        break;
    case GL_RGB8UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_RGB16UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_RGB32UI:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_RGB8I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_RGB16I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_RGB32I:
        dstcomponent = GL_RGB_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_RGB32F:
        dstcomponent = GL_RGB;
        dstformat = GL_FLOAT;
        break;
    case GL_RGBA8UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_BYTE;
        break;
    case GL_RGBA16UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_SHORT;
        break;
    case GL_RGBA32UI:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_UNSIGNED_INT;
        break;
    case GL_RGBA8I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_BYTE;
        break;
    case GL_RGBA16I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_SHORT;
        break;
    case GL_RGBA32I:
        dstcomponent = GL_RGBA_INTEGER;
        dstformat = GL_INT;
        break;
    case GL_RG32F:
        dstcomponent = GL_RG;
        dstformat = GL_FLOAT;
        break;
    case GL_RGBA32F:
        dstcomponent = GL_RGBA;
        dstformat = GL_FLOAT;
        break;
        //    default:
        //        texf = GL_RGBA32F;
        //        break;
    }

    GLContextSZ * ctx =  reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,srctex,0);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;    //最少要附加一个颜色缓冲附件
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        glBindBuffer(GL_PIXEL_PACK_BUFFER,0);
        return -1;
    }

    glReadBuffer(GL_COLOR_ATTACHMENT0);
    glReadPixels(0, 0,  //offset position
                 w, h,
                 dstcomponent,
                 dstformat,
                 nullptr    //当 GL_PIXEL_PACK_BUFFER有绑定对象时，这个参数相当于读取的纹理数据放到PBO的偏移字节地址
                 );

    glBindBuffer(GL_PIXEL_PACK_BUFFER,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);


    GLenum err = glGetError();
    if(err){
        std::cerr<<"opengl error may be cause by glgpAsyncCopyTex2DtoPBO! error code = "<< err <<std::endl;
    }
    return static_cast<int>(err);
}

int glgpDeleteGLBufferObj(unsigned int bufid)
{
    if(!bufid) return -1;
    if(glIsBuffer(bufid) != GL_TRUE){
        std::cerr<<"glgpDeleteGLBufferObj input is not a buffer object!"<<std::endl;
        return -1;
    }
    glDeleteBuffers(1,&bufid);
    return static_cast<int>(glGetError());
}


int glgpDeleteGLBufferObjects(int len, unsigned int *bufids)
{
    if(len < 1 || !bufids) return -1;
    glDeleteBuffers(len,bufids);
    return static_cast<int>(glGetError());
}



void *glgpGetFenceSync()
{
    return glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE,0);
}

void glgpDeleteSync(void *sync){
    if(sync) glDeleteSync(reinterpret_cast<GLsync>(sync));
}

int glgpClientWaitSync(void *sync, unsigned long long timeout_ns)
{
    if(!glIsSync(reinterpret_cast<GLsync>(sync))) return -1;
    glClientWaitSync(reinterpret_cast<GLsync>(sync),GL_SYNC_FLUSH_COMMANDS_BIT,timeout_ns);
    GLint signal = GL_UNSIGNALED;
    glGetSynciv(reinterpret_cast<GLsync>(sync),GL_SYNC_STATUS,1,nullptr,&signal);
    return !(signal == GL_SIGNALED );
}

int glgpClientWait(unsigned long long timeout_ns){
    GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE,0);
    if(!glIsSync(fence)){
        glDeleteSync(fence);
        return -1;
    }
    glClientWaitSync(fence,GL_SYNC_FLUSH_COMMANDS_BIT,timeout_ns);
    GLint signal = GL_UNSIGNALED;
    glGetSynciv(fence,GL_SYNC_STATUS,1,nullptr,&signal);
    glDeleteSync(fence);
    return !(signal == GL_SIGNALED );
}


int glgpCvtUItoFloat(unsigned int srctex, unsigned int dsttex,
                     float alpha, float beta,int colormask)        //int width,int height,
{

    int width=0,height=0;

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    glBindTexture(GL_TEXTURE_2D,0);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    GLuint &program = ctx->programs[_ShaderProgramIndex::_SPI_CvtUItoFloat];
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    glUseProgram(program);

    glUniform1i(0,0);   //设置源纹理的 sample2D对应GL_TEXTURE0
    glUniform1f(20,alpha);
    glUniform1f(21,beta);


    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);

    glViewport(0, 0, width, height);
    GLenum outputs[] = {GL_COLOR_ATTACHMENT0};
    glDrawBuffers(1,outputs);   //gl es 没有glDrawBuffer

    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindTexture(GL_TEXTURE_2D,0);
    glBindVertexArray(0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    return static_cast<int>(glGetError());
}



int glgpCvtFloatToUI(unsigned int srctex, unsigned int dsttex,
                      float alpha, float beta,int colormask)    //int width, int height,
{
    int width=0,height=0;

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    glBindTexture(GL_TEXTURE_2D,0);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    GLuint &program = ctx->programs[_ShaderProgramIndex::_SPI_CvtFloatToUI];
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    glUseProgram(program);

    glUniform1i(0,0);   //设置源纹理的 sample2D对应GL_TEXTURE0
    glUniform1f(20,alpha);
    glUniform1f(21,beta);


    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);

    glViewport(0, 0, width, height);
    GLenum outputs[] = {GL_COLOR_ATTACHMENT0};
    glDrawBuffers(1,outputs);   //gl es 没有glDrawBuffer

    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindTexture(GL_TEXTURE_2D,0);
    glBindVertexArray(0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    return static_cast<int>(glGetError());
}

int glgpBlitTexture2D(unsigned int srctex, unsigned int dsttex)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;
    if(srctex == dsttex){
        std::cerr<<"glgpBlitTexture2D srctex and dsttex can not be same!"<<std::endl;
        return -1;
    }

    int width = 0,height=0;//,format=GL_FLOAT;
    GLenum drawdst = GL_NONE;


    GLContextSZ * ctx =  reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
//     glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&GL_FLOAT);
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_READ_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D( GL_READ_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,srctex,0);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    drawdst = GL_COLOR_ATTACHMENT1;
    glDrawBuffers(1,&drawdst);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBlitFramebuffer(0,0,width,height,
                      0,0,width,height,
                      GL_COLOR_BUFFER_BIT,
                      GL_NEAREST
                      );


    glFramebufferTexture2D( GL_READ_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER,0);
    glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER,0);

    GLenum err = glGetError();
    if(err){
        std::cerr<<"glgpBlitTexture2D may be error, error code = "<<err<<std::endl;
    }

    return static_cast<int>(err);
}

int glgpTextureCopy(unsigned int srctex, unsigned int dsttex, float alpha, float beta, int colormask)
{
    int width=0,height=0,itype = GL_UNSIGNED_INT,otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (srctex == dsttex), medtexformat=0;;
    GLContextSZ * ctx =  reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    //获取输出纹理尺寸和数据类型
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);       //由于要使用到color mask,所以必须要复制源纹理到中间缓冲纹理中
    }
    // GL_TEXTURE_RED_TYPE 只返回
    // GL_NONE, GL_SIGNED_NORMALIZED, GL_UNSIGNED_NORMALIZED,
    // GL_FLOAT, GL_INT, and GL_UNSIGNED_INT

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    if(otype == GL_INT){
        otype = 2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
        drawdst[2] = GL_COLOR_ATTACHMENT2;
    }else if(otype == GL_UNSIGNED_INT){
        otype = 1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
        drawdst[1] = GL_COLOR_ATTACHMENT1;
    }else if(otype == GL_FLOAT){
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }else{
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }

    glBindTexture(GL_TEXTURE_2D,0);

    //获取输入纹理数据类型
    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    if(itype == GL_INT){
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        itype = 1;
    }else if(itype == GL_FLOAT){
        itype = 0;
    }else{
        itype = 0;
    }
    glBindTexture(GL_TEXTURE_2D,0);

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TextureCopy]);

    glUniform1i(0,0);   //设置源纹理的 sample2D对应GL_TEXTURE0
    glUniform1i(1,1);
    glUniform1i(2,2);
    glUniform1f(20,alpha);
    glUniform1f(21,beta);
    GLint iotype[] = {itype,otype};
    glUniform2iv(22,1,iotype);      //设置输入输出类型

    if(itype==0) glActiveTexture(GL_TEXTURE0);
    if(itype==1) glActiveTexture(GL_TEXTURE1);
    if(itype==2) glActiveTexture(GL_TEXTURE2);


    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);

    glViewport(0, 0, width, height);

    glDrawBuffers(3,drawdst);   //gl es 没有glDrawBuffer

//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }

    return static_cast<int>(glGetError());
}


int glgpTextureCopySrc(unsigned int srctex, unsigned int dsttex, int xoffset,int yoffset, int colormask)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;
    if(srctex == dsttex){
        std::cerr<<"glgpTextureCopySrc srctex and dsttex can not be same!"<<std::endl;
        return -1;
    }

    int width=0,height=0,itype = GL_UNSIGNED_INT,otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};

    GLContextSZ * ctx =  reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    //获取输出纹理尺寸和数据类型
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    // GL_TEXTURE_RED_TYPE 只返回
    // GL_NONE, GL_SIGNED_NORMALIZED, GL_UNSIGNED_NORMALIZED,
    // GL_FLOAT, GL_INT, and GL_UNSIGNED_INT

    if(otype == GL_INT){
        otype = 2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
        drawdst[2] = GL_COLOR_ATTACHMENT2;
    }else if(otype == GL_UNSIGNED_INT){
        otype = 1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
        drawdst[1] = GL_COLOR_ATTACHMENT1;
    }else if(otype == GL_FLOAT){
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }else{
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }

    glBindTexture(GL_TEXTURE_2D,0);

    //获取输入纹理数据类型
    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    if(itype == GL_INT){
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        itype = 1;
    }else if(itype == GL_FLOAT){
        itype = 0;
    }else{
        itype = 0;
    }
    glBindTexture(GL_TEXTURE_2D,0);

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TextureCopySrc]);

    glUniform1i(0,0);   //设置源纹理的 sample2D对应GL_TEXTURE0
    glUniform1i(1,1);
    glUniform1i(2,2);
    glUniform2i(23,xoffset,yoffset);
    GLint iotype[] = {itype,otype};
    glUniform2iv(22,1,iotype);      //设置输入输出类型

    if(itype==0) glActiveTexture(GL_TEXTURE0);
    if(itype==1) glActiveTexture(GL_TEXTURE1);
    if(itype==2) glActiveTexture(GL_TEXTURE2);


    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);

    glViewport(0, 0, width, height);

    glDrawBuffers(3,drawdst);   //gl es 没有glDrawBuffer

//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    return static_cast<int>(glGetError());
}


int glgpSplitTexture(unsigned int srctex, unsigned int dsttexR, unsigned int dsttexG, unsigned int dsttexB, unsigned int dsttexA)
{
     int width=0,height=0, itype = GL_UNSIGNED_INT;
     GLenum drawdst[4] = {GL_NONE};
     GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                 glfwGetWindowUserPointer(glfwGetCurrentContext())
                 );

     //获取源纹理尺寸和数据类型
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D,srctex);

     glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
     glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
     glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
//     glBindTexture(GL_TEXTURE_2D,0);

     glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
     if(dsttexR){
         drawdst[0] = GL_COLOR_ATTACHMENT0;
         glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttexR,0);
     }
     if(dsttexG){
         drawdst[1] = GL_COLOR_ATTACHMENT1;
         glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttexG,0);
     }
     if(dsttexB){
         drawdst[2] = GL_COLOR_ATTACHMENT2;
         glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttexB,0);
     }
     if(dsttexA){
         drawdst[3] = GL_COLOR_ATTACHMENT3;
         glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT3,GL_TEXTURE_2D,dsttexA,0);
     }

     //检查帧缓冲区是否有效
     if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
     {
         std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
         return -1;
     }

     if(itype == GL_INT){
         glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_SplitTextureINT]);
     }else if(itype == GL_UNSIGNED_INT){
         glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_SplitTextureUI]);
     }else{
         glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_SplitTextureFloat]);
     }

     glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_FALSE);

     glViewport(0, 0, width, height);

     glDrawBuffers(4,drawdst);

     glClearColor(0,0,0,0);
     glClear(GL_COLOR_BUFFER_BIT);


     glBindVertexArray(ctx->square_vao);
     glDrawArrays(GL_TRIANGLES, 0, 6);

     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D,0);
     glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
     glBindVertexArray(0);
     glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
     glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
     glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
     glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT3,GL_TEXTURE_2D,0,0);
     glBindFramebuffer(GL_FRAMEBUFFER,0);
     glUseProgram(0);

     return static_cast<int>(glGetError());
}

int glgpMergeTextures(unsigned int dsttex,
                      unsigned int srctexR, unsigned int srctexG, unsigned int srctexB, unsigned int srctexA)
{
    int width=0,height=0, otype = GL_UNSIGNED_INT;
    const GLenum drawdst[1] = {GL_COLOR_ATTACHMENT0};
    unsigned int srctexs[4] = {srctexR,srctexG,srctexB,srctexA};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);

    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    if(otype == GL_INT){
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_MergeTexturesINT]);
    }else if(otype == GL_UNSIGNED_INT){
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_MergeTexturesUI]);
    }else{
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_MergeTexturesFloat]);
    }

    for (int i = 0; i < 4; ++i) {
        glUniform1i(i,i);
        if(srctexs[i]){
            glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));

            glBindTexture(GL_TEXTURE_2D,srctexs[i]);
        }else{
            glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));
            glBindTexture(GL_TEXTURE_2D,0);
        }
    }

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(1,drawdst);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT);


    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);


    for (int i = 0; i < 4; ++i) {
        glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));
        glBindTexture(GL_TEXTURE_2D,0);
    }
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    return static_cast<int>(glGetError());
}


int glgpResizeTexture(unsigned int srctex,unsigned int dsttex,int interpolationMode){
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;
    if(srctex == dsttex){
        std::cerr<<"glgpResizeTexture srctex and dsttex can not be same!"<<std::endl;
        return -1;
    }

    GLuint srctexbuf = 0;   //输入纹理浮点缓冲纹理
    int width=0,height=0, otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    //获取源纹理尺寸
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glBindTexture(GL_TEXTURE_2D,0);

    //新建输入浮点纹理缓冲，以便于使用opengl自动插值功能
    if(interpolationMode == 0) interpolationMode = GL_LINEAR;
    else interpolationMode = GL_NEAREST;
    glGenTextures(1 , &srctexbuf);
    glBindTexture(GL_TEXTURE_2D, srctexbuf);
    glTexStorage2D(GL_TEXTURE_2D, 1 , GL_RGBA32F , width , height );  // glTexStorage2D一般用于创建不可变纹理,不可变纹理才能绑定到image load/store单元
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interpolationMode);      //GL_NEAREST
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interpolationMode);       //GL_LINEAR wiki上说只支持浮点数类型的纹理自动插值
    glBindTexture(GL_TEXTURE_2D, 0);
    glgpTextureCopy(srctex,srctexbuf,1,0,0);    //复制到缓冲区


    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);

    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
    }else{
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    }

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        glDeleteTextures(1,&srctexbuf);
        return -1;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_ResizeTextures]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, srctexbuf);
//    if(otype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
//        glActiveTexture(GL_TEXTURE2);

//        glBindTexture(GL_TEXTURE_2D,srctex);
//    }else if(otype == GL_UNSIGNED_INT){
//        glActiveTexture(GL_TEXTURE1);

//        glBindTexture(GL_TEXTURE_2D,srctex);
//    }else{
//        glActiveTexture(GL_TEXTURE0);

//        glBindTexture(GL_TEXTURE_2D,srctex);
//    }
    glUniform1i(0,0);       //glsl 中的纹理单元映射
//    glUniform1i(1,1);
//    glUniform1i(2,2);

    glUniform2f(20,static_cast<GLfloat>(width),static_cast<GLfloat>(height));   //设置宽高

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    glDeleteTextures(1,&srctexbuf);

    return static_cast<int>(glGetError());
}


int glgpFillTexture(unsigned tex, float r, float g, float b, float a, int colormask)
{
    if(tex == 0 ) return -1;
    if((colormask & 0x0f) == 0x0f) return 0;

    GLint width=0,height=0,dsttype = GL_FLOAT;
    const GLenum drawdst[1] = {GL_COLOR_ATTACHMENT0};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    //获取目标纹理尺寸
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,tex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&dsttype);
    glBindTexture(GL_TEXTURE_2D,0);

    if(dsttype == GL_INT){
        *(reinterpret_cast<int *>(&r)) = static_cast<int>(r);
        *(reinterpret_cast<int *>(&g)) = static_cast<int>(g);
        *(reinterpret_cast<int *>(&b)) = static_cast<int>(b);
        *(reinterpret_cast<int *>(&a)) = static_cast<int>(a);
    }else if(dsttype == GL_UNSIGNED_INT){
        *(reinterpret_cast<unsigned int *>(&r)) = static_cast<unsigned int>(r);
        *(reinterpret_cast<unsigned int *>(&g)) = static_cast<unsigned int>(g);
        *(reinterpret_cast<unsigned int *>(&b)) = static_cast<unsigned int>(b);
        *(reinterpret_cast<unsigned int *>(&a)) = static_cast<unsigned int>(a);
    }

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,tex,0);

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(1,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
    glClearColor(r,g,b,a);
    glClear(GL_COLOR_BUFFER_BIT);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    return static_cast<int>(glGetError());
}


int glgpTextureThreshold(unsigned int srctex, unsigned int dsttex,
                         float thresh, float maxval, int type,
                         int colormask)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;

    int width=0,height=0, otype = GL_UNSIGNED_INT;
    const GLenum drawdst[1] = {GL_COLOR_ATTACHMENT0};
    int needmedtex = (dsttex == srctex),medtexformat=0;
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);
    }
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    if(otype == GL_INT){
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexturesThresholdINT]);
        GLint th = static_cast<GLint>(thresh),max = static_cast<GLint>(maxval);
        glUniform4i(21,th,th,th,th); //  设置阈值
        glUniform4i(22,max,max,max,max); //  设置最大值
    }else if(otype == GL_UNSIGNED_INT){
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexturesThresholdUI]);
        GLuint th = static_cast<GLuint>(thresh),max = static_cast<GLuint>(maxval);
        glUniform4ui(21,th,th,th,th); //  设置阈值
        glUniform4ui(22,max,max,max,max); //  设置最大值
    }else{
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexturesThresholdFloat]);
        glUniform4f(21,thresh,thresh,thresh,thresh); //  设置阈值
        glUniform4f(22,maxval,maxval,maxval,maxval); //  设置最大值
    }
    glUniform1i(20,type);   // 设置阈值运算模式

    //设置源纹理的 sample2D GL_TEXTURE0单元
    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(1,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);


    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }


    return static_cast<int>(glGetError());
}

int glgpTextureROI(unsigned int srctex, unsigned int dsttex,
                   int xoffset, int yoffset, int colormask)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;
    if(srctex == dsttex){
        std::cerr<<"glgpTextureROI srctex and dsttex can not be same!"<<std::endl;
        return -1;
    }

    int width=0,height=0, otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);

    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
    }else{
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    }

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        return -1;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexturesROI]);
    if(otype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
        glActiveTexture(GL_TEXTURE2);

        glBindTexture(GL_TEXTURE_2D,srctex);
    }else if(otype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);

        glBindTexture(GL_TEXTURE_2D,srctex);
    }else{
        glActiveTexture(GL_TEXTURE0);

        glBindTexture(GL_TEXTURE_2D,srctex);
    }
    glUniform1i(0,0);       //glsl 中的纹理单元映射
    glUniform1i(1,1);
    glUniform1i(2,2);

    glUniform2i(20,xoffset,yoffset);    //设置偏移

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    return static_cast<int>(glGetError());
}



int glgpTexelMath(unsigned int srctex, unsigned int dsttex,
                  float alpha, float beta, int mode, int colormask)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;

    int width=0,height=0, otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (dsttex == srctex),medtexformat=0;
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);
    }
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
    }else{
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    }

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexelMath]);
    if(otype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
        glActiveTexture(GL_TEXTURE2);

        glBindTexture(GL_TEXTURE_2D,srctex);
        glUniform2i(22,2,2);
    }else if(otype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);

        glBindTexture(GL_TEXTURE_2D,srctex);
        glUniform2i(22,1,1);
    }else{
        glActiveTexture(GL_TEXTURE0);

        glBindTexture(GL_TEXTURE_2D,srctex);
        glUniform2i(22,0,0);
    }
    glUniform1i(0,0);       //glsl 中的纹理单元映射
    glUniform1i(1,1);
    glUniform1i(2,2);

    glUniform1f(20,alpha);    //设置常量
    glUniform1f(21,beta);
    glUniform1i(23,mode);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }
    return static_cast<int>(glGetError());
}

int glgpTexturesMath(unsigned int srctex, unsigned int srctex1, unsigned int dsttex,
                     int mode, int colormask)
{
    int width=0,height=0, otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (dsttex == srctex) || (dsttex == srctex1),medtexformat=0;
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    //获取目标纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);
    }
    glBindTexture(GL_TEXTURE_2D,0);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
    }else{
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    }

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexturesMath]);
    if(otype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
        glActiveTexture(GL_TEXTURE2);

        glBindTexture(GL_TEXTURE_2D,srctex);

        glActiveTexture(GL_TEXTURE5);

        glBindTexture(GL_TEXTURE_2D,srctex1);
        glUniform1i(22,2);      //纹理的数据类型
    }else if(otype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);

        glBindTexture(GL_TEXTURE_2D,srctex);

        glActiveTexture(GL_TEXTURE4);

        glBindTexture(GL_TEXTURE_2D,srctex1);
        glUniform1i(22,1);
    }else{
        glActiveTexture(GL_TEXTURE0);

        glBindTexture(GL_TEXTURE_2D,srctex);

        glActiveTexture(GL_TEXTURE3);

        glBindTexture(GL_TEXTURE_2D,srctex1);
        glUniform1i(22,0);
    }

    for (int i = 0; i < 6; ++i) {
        glUniform1i(i,i);       //glsl 中的纹理单元映射
    }

    glUniform1i(23,mode); //设置常量

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    for (int i = 5; i >= 0; --i) {
        glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));
        glBindTexture(GL_TEXTURE_2D,0);
    }
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }

    return static_cast<int>(glGetError());
}

int glgpWarpPerspective(unsigned int srctex, unsigned int dsttex, float *mat3x3)
{
    if(srctex * dsttex == 0) return -1;

    float transmat[3*3];
    if( _calcInvertedMatrix(mat3x3,transmat,3) ){
        std::cout<<"invalid transform matrix,can not inverse!"<<std::endl;
    }

    GLuint floattexbuf = 0; //临时四通道32位浮点纹理缓冲区，用于暂存结果
    GLuint texsampler = 0;  //输和纹理的采样器，用于修改代替纹理的采样设置
    int width=0,height=0,texformat = GL_FLOAT, itype = GL_UNSIGNED_INT;
    GLenum drawdst[1] = {GL_NONE};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    bool samesrcdst = false;
    if(dsttex == srctex) samesrcdst = true;

    //获取源纹理尺寸和数据类型
    glActiveTexture(GL_TEXTURE0);
    if(samesrcdst) glBindTexture(GL_TEXTURE_2D,srctex);
    else glBindTexture(GL_TEXTURE_2D,dsttex);  //如果有目标纹理，以目标纹理的参数为准
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&texformat);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    glBindTexture(GL_TEXTURE_2D,0);

    floattexbuf = glgpAllocateGLTexture2D(GLF_RGBA32F,width,height,nullptr);    //新建一个32位浮点纹理用于暂存结果
    //https://www.khronos.org/registry/OpenGL-Refpages/es3.1/
    glBindImageTexture(0,floattexbuf,0,GL_FALSE,0,GL_READ_WRITE,GL_RGBA32F);    //绑定到图像load storage 0单元

    glGenSamplers(1,&texsampler);
    glSamplerParameteri(texsampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(texsampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(texsampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);     //改为 GL_NEAREST 在着色器代码中实现插值吧,这样通用性强一点点,免得GL_TEXTURE_BORDER_COLOR以后版本采样出来不是(0,0,0,0)
    glSamplerParameteri(texsampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//    float borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };                     //新的opengl版本已经弃用了纹理边界这个属性,但这里还是设置一下吧,希望可以使用GL_LINEAR自动插值边界，减小边界锯齿
//    glSamplerParameterfv(texsampler, GL_TEXTURE_BORDER_COLOR,borderColor);

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,floattexbuf,0);    //必须绑定一个颜色缓冲 否则不执行着色器
    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_WRITE,GL_RGBA32F);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glDeleteTextures(1,&floattexbuf);
        return -1;
    }


    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TextureTransform]);
    if(itype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D,srctex);
        glBindSampler(2, texsampler); // 绑定采样器到纹理单元

        glUniform1i(21,2);  //设置输入纹理的数据类型
    }else if(itype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,srctex);
        glBindSampler(1, texsampler);

        glUniform1i(21,1);
    }else{
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,srctex);
        glBindSampler(0, texsampler);

        glUniform1i(21,0);
    }
    glUniform1i(0,0);       //glsl 中的纹理单元映射
    glUniform1i(1,1);
    glUniform1i(2,2);

//    float w=static_cast<float>(width),h=static_cast<float>(height);
//    float projmat[3][3] = {
//        {2.0f/w,    0, 0},
//        {0,     2.0f/h,0},
//        {-1,        -1,1},
//    };
//    float viewmat[3][3] = {
//        {1, 0, 0},
//        {0, 1, 0},
//        {0, 0, 1},
//    };
//    float modelmat[3][3] = {
//        {(w-1.0f)/2.0f   ,0             ,0},
//        {0,              (h-1.0f)/2.0f  ,0},
//        {(w-1.0f)/2.0f,  (h-1.0f)/2.0f  ,1},
//    };

    glUniform2i(20,width,height);   //设置源纹理宽度

//    glUniformMatrix3fv(40,1,GL_FALSE,&projmat[0][0]);   //设置3x3变换矩阵
    glUniformMatrix3fv(41,1,GL_FALSE,transmat);   //&viewmat[0][0] mat3x3
//    glUniformMatrix3fv(42,1,GL_FALSE,&modelmat[0][0]);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(1,drawdst);

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindSampler(2, 0);
    glBindSampler(1, 0);
    glBindSampler(0, 0);

    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(samesrcdst){
        glgpTextureCopy(floattexbuf,srctex,1,0,0);
    }else{
        glgpTextureCopy(floattexbuf,dsttex,1,0,0);
    }
    glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_WRITE,GL_RGBA32F);
    glDeleteTextures(1,&floattexbuf);
    glDeleteSamplers(1,&texsampler);

    return static_cast<int>(glGetError());
}

int glgpWarpAffine(unsigned int srctex, unsigned int dsttex, float *mat2x3)
{
    float buf[3][3];
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            buf[j][i] = mat2x3[j*3 + i];
        }
    }
    buf[0][2] = 0;
    buf[1][2] = 0;
    buf[2][2] = 1;
    return glgpWarpPerspective(srctex,dsttex,&buf[0][0]);
}


int glgpGetPerspectiveTransform(float *srcpoint,float *dstpoint,float *result){
    if(!srcpoint || !dstpoint || !result){
        return -1;
    }
    float *combinmatrixbuf = new float[8*9];
    float (*combinmatrix)[8][9] = reinterpret_cast<float (*)[8][9]>(combinmatrixbuf);   //放置8行9列大矩阵
    float (*srcp)[4][2] = reinterpret_cast<float (*)[4][2]>(srcpoint);
    float (*dstp)[4][2] = reinterpret_cast<float (*)[4][2]>(dstpoint);

    for (int r = 0; r < 4; ++r) {   //填充前面四行
        (*combinmatrix)[r][8] = (*dstp)[r][0];  //u1~u4
        (*combinmatrix)[r][0] = (*srcp)[r][0];  //x1~x4
        (*combinmatrix)[r][1] = (*srcp)[r][1];  //y1~y4
        (*combinmatrix)[r][2] = 1;  //
        (*combinmatrix)[r][3] = 0;
        (*combinmatrix)[r][4] = 0;
        (*combinmatrix)[r][5] = 0;
        (*combinmatrix)[r][6] = -(*srcp)[r][0]*(*dstp)[r][0];  //-x1*u1 ~ -x4*u4
        (*combinmatrix)[r][7] = -(*srcp)[r][1]*(*dstp)[r][0];  //-y1*u1 ~ -y4*u4
    }

    for (int r = 4; r < 8; ++r) {   //填充后面四行
        (*combinmatrix)[r][8] = (*dstp)[r-4][1];  //v1~v4
        (*combinmatrix)[r][0] = 0;
        (*combinmatrix)[r][1] = 0;
        (*combinmatrix)[r][2] = 0;  //
        (*combinmatrix)[r][3] = (*srcp)[r-4][0];  //x1~x4
        (*combinmatrix)[r][4] = (*srcp)[r-4][1];  //y1~y4
        (*combinmatrix)[r][5] = 1;
        (*combinmatrix)[r][6] = -(*srcp)[r-4][0]*(*dstp)[r-4][1];  //-x1*v1 ~ -x4*v4
        (*combinmatrix)[r][7] = -(*srcp)[r-4][1]*(*dstp)[r-4][1];  //-y1*v1 ~ -y4*v4
    }

//    std::cout<<"---------------------------------------"<<std::endl;
//    for (int r = 0; r < 8; ++r) {
//        for (int c = 0; c < 9; ++c) {
//            std::cout<<(*combinmatrix)[r][c]<<", ";
//        }
//        std::cout<<std::endl;
//    }
//    std::cout<<"---------------------------------------"<<std::endl;

    float *resbuf = new float[9];
    int err = _GaussEliminationSolveMat(combinmatrixbuf,8,9,resbuf);

//    for (int r = 0; r < 8; ++r) {
//        for (int c = 0; c < 9; ++c) {
//            std::cout<<(*combinmatrix)[r][c]<<", ";
//        }
//        std::cout<<std::endl;
//    }
//    std::cout<<"---------------------------------------"<<std::endl;

    if(err){
        for (int r = 0; r < 3; ++r) {
            for (int c = 0; c < 3; ++c) {
                resbuf[r*3 + c] = 0;
            }
        }
        resbuf[0*3+0] = 1;
        resbuf[1*3+1] = 1;
        resbuf[2*3+2] = 1;
        resbuf[8] = 1;
    }

    for (int r = 0; r < 3; ++r) {        //列优先放置到结果内存
        for (int c = 0; c < 3; ++c) {
            if((r*3 + c) == 8) continue;
            result[c*3 + r] = resbuf[r*3 + c];
        }
    }
    result[8] = 1;

    delete [] resbuf;
    delete [] combinmatrixbuf;
    return err;
}

int glgpGetAffineTransform(float *srcpoint, float *dstpoint, float *result)
{
    if(!srcpoint || !dstpoint || !result){
        return -1;
    }
    float *combinmatrixbuf = new float[6*7];
    float (*combinmatrix)[6][7] = reinterpret_cast<float (*)[6][7]>(combinmatrixbuf);   //放置6行7列大矩阵
    float (*srcp)[3][2] = reinterpret_cast<float (*)[3][2]>(srcpoint);
    float (*dstp)[3][2] = reinterpret_cast<float (*)[3][2]>(dstpoint);

    for (int r = 0; r < 3; ++r) {   //填充前面三行
        (*combinmatrix)[r][6] = (*dstp)[r][0];  //u1~u4
        (*combinmatrix)[r][0] = (*srcp)[r][0];  //x1~x4
        (*combinmatrix)[r][1] = (*srcp)[r][1];  //y1~y4
        (*combinmatrix)[r][2] = 1;  //
        (*combinmatrix)[r][3] = 0;
        (*combinmatrix)[r][4] = 0;
        (*combinmatrix)[r][5] = 0;
    }

    for (int r = 3; r < 6; ++r) {   //填充后面三行
        (*combinmatrix)[r][6] = (*dstp)[r-3][1];  //v1~v4
        (*combinmatrix)[r][0] = 0;
        (*combinmatrix)[r][1] = 0;
        (*combinmatrix)[r][2] = 0;  //
        (*combinmatrix)[r][3] = (*srcp)[r-3][0];  //x1~x4
        (*combinmatrix)[r][4] = (*srcp)[r-3][1];  //y1~y4
        (*combinmatrix)[r][5] = 1;
    }


//        std::cout<<"---------------------------------------"<<std::endl;
//        for (int r = 0; r < 6; ++r) {
//            for (int c = 0; c < 7; ++c) {
//                std::cout<<(*combinmatrix)[r][c]<<", ";
//            }
//            std::cout<<std::endl;
//        }
//        std::cout<<"---------------------------------------"<<std::endl;

    float resbuf[6] = {0};
    int err = _GaussEliminationSolveMat(combinmatrixbuf,6,7,resbuf);

//        for (int r = 0; r < 6; ++r) {
//            for (int c = 0; c < 7; ++c) {
//                std::cout<<(*combinmatrix)[r][c]<<", ";
//            }
//            std::cout<<std::endl;
//        }
//        std::cout<<"---------------------------------------"<<std::endl;

    if(err){
        for (int r = 0; r < 2; ++r) {
            for (int c = 0; c < 3; ++c) {
                resbuf[r*3 + c] = 0;
            }
        }
        resbuf[0*3+0] = 1;
        resbuf[1*3+1] = 1;
    }

    for (int r = 0; r < 2; ++r) {       //列优先放置到结果内存
        for (int c = 0; c < 3; ++c) {
            result[c*3 + r] = resbuf[r*3 + c];
        }
    }

    delete [] combinmatrixbuf;
    return err;
}

const char * glgpGet2DFFTShaderCode(){
    return compute2DFFTTexShaderStr;
}

int glgpReplace2DFFTProgram(unsigned int program){
    if(program!=0 && glIsProgram(program) != GL_TRUE){
        std::cerr<<"glgpReplace2DFFTProgram receive not program name!"<<std::endl;
        return -1;
    }

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    ctx->programs[_SPI_Custom2DTexFFT] = program;

    return 0;
}

int glgpCreateTex2DFFTPlan(unsigned int tex, GLGP_FFTPlan2D *plan)
{
    if(!tex || !plan) return -1;

    const int maxlen = 65536;   //单方向fft最大的数据长度
    GLGP_FFTPlan2D_SZ ptemp;
    int width=0,height=0;
    glBindTexture(GL_TEXTURE_2D,tex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glBindTexture(GL_TEXTURE_2D,0);

    if(width < 1  || height < 1) return -1;
    if(width < 2  && height < 2) return -1;
    if(width > maxlen  || height > maxlen) return -1;

    ptemp.width = width;
    ptemp.height = height;
    for (int i = 2; i < maxlen; i = i*2) {
        ptemp.colbitcnt++;
        if(i >= width){
            width = i;
            break;
        }
    }

    for (int i = 2; i < maxlen; i = i*2) {
        ptemp.rowbitcnt++;
        if(i >= height){
            height = i;
            break;
        }
    }

    float *dataf = new float[static_cast<size_t>(width * 2)];

    //新建窗函数数据缓冲纹理
    for (int i = 0; i < width; ++i) {
        dataf[2*i + 1] = 0;     //默认复数部分全为0
        if(i<ptemp.width){
            dataf[2*i + 0] = 1; //窗函数默认实部有数据部分才取1
        }else{
            dataf[2*i + 0] = 0;
        }
    }
    glGenTextures(1 , &ptemp.fftWINBuf[0]);
    glBindTexture(GL_TEXTURE_2D, ptemp.fftWINBuf[0]);
    glTexStorage2D(GL_TEXTURE_2D, 1 ,GL_RG32F , width,1);
    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0,width,1,
                    GL_RG,GL_FLOAT,dataf
                    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);
    delete [] dataf;

    dataf = new float[static_cast<size_t>(height * 2)];
    for (int i = 0; i < height; ++i) {
         dataf[2*i + 1] = 0;     //默认复数部分全为0
        if(i<ptemp.height){
            dataf[2*i + 0] = 1; //窗函数默认实部有数据部分才取1
        }else{
            dataf[2*i + 0] = 0;
        }
    }
    glGenTextures(1 , &ptemp.fftWINBuf[1]);
    glBindTexture(GL_TEXTURE_2D, ptemp.fftWINBuf[1]);
    glTexStorage2D(GL_TEXTURE_2D, 1 ,GL_RG32F , height, 1);
    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0,height,1,
                    GL_RG,GL_FLOAT,dataf
                    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);
    delete [] dataf;

    //新建计算好的wkN正交旋转因子缓冲纹理
    dataf = new float[static_cast<size_t>(width)];
    for (int k = 0; k < width/2; ++k) {
        float d = -2 * M_PI_F * k;
        d = d / static_cast<float>(width);
        dataf[2*k + 0] = cosf(d);   //实部
        dataf[2*k + 1] = sinf(d);  //虚部
    }
    glGenTextures(1 , &ptemp.wkBuf[0]);
    glBindTexture(GL_TEXTURE_2D, ptemp.wkBuf[0]);
    glTexStorage2D(GL_TEXTURE_2D, 1 ,GL_RG32F , width / 2 ,1);
    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0,width / 2,1,
                    GL_RG,GL_FLOAT,dataf
                    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBindTexture(GL_TEXTURE_2D, 0);
    delete [] dataf;

    dataf = new float[static_cast<size_t>(height)];
    for (int k = 0; k < height/2; ++k) {
        float d = -2 * M_PI_F * k;
        d = d / static_cast<float>(height);
        dataf[2*k + 0] = cosf(d);   //实部
        dataf[2*k + 1] = sinf(d);  //虚部
    }
    glGenTextures(1 , &ptemp.wkBuf[1]);
    glBindTexture(GL_TEXTURE_2D, ptemp.wkBuf[1]);
    glTexStorage2D(GL_TEXTURE_2D, 1 ,GL_RG32F , height / 2, 1);
    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0, height / 2, 1,
                    GL_RG,GL_FLOAT,dataf
                    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBindTexture(GL_TEXTURE_2D, 0);
    delete [] dataf;


    //设置计算缓冲区宽高
    ptemp.width = width;
    ptemp.height = height;

    // 新建计算时用到的缓冲区数组
    glGenTextures(1 ,&ptemp.complexBufs);
    glBindTexture(GL_TEXTURE_2D_ARRAY,ptemp.complexBufs);
    glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1 ,GL_R32F ,
                   width * 2, height,
                   2    //数组大小为2
                   );
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_REPEAT);
    glBindTexture(GL_TEXTURE_2D_ARRAY,0);

    //返回创建好的fft 计划指针
    GLGP_FFTPlan2D_SZ *planbuf = new GLGP_FFTPlan2D_SZ;
    *planbuf = ptemp;
    *plan = reinterpret_cast<GLGP_FFTPlan2D>(planbuf);

    return 0;
}

int glgpReleaseTex2DFFTPlan(GLGP_FFTPlan2D plan)
{
    if(plan == nullptr) return -1;

    GLGP_FFTPlan2D_SZ *planbuf = reinterpret_cast<GLGP_FFTPlan2D_SZ *>(plan);
    glDeleteTextures(1,&(planbuf->complexBufs));
    glDeleteTextures(2,planbuf->fftWINBuf);
    glDeleteTextures(2,planbuf->wkBuf);

    delete planbuf;
    return 0;
}


int glgpGetTex2DFFTPlanOptimalSize(GLGP_FFTPlan2D plan, int *width, int *height)
{
    if(plan == nullptr || width == nullptr || height == nullptr) return -1;

    GLGP_FFTPlan2D_SZ *planbuf = reinterpret_cast<GLGP_FFTPlan2D_SZ *>(plan);
    *width = planbuf->width;
    *height = planbuf->height;

    return 0;
}


int glgpExecTex2DFFTPlan(GLGP_FFTPlan2D ptrplan, unsigned int srctex, unsigned dsttex, int fftmode, int fftdir, float alpha)
{
    int width = 0, height = 0;
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    GLGP_FFTPlan2D_SZ &plan = *reinterpret_cast<GLGP_FFTPlan2D_SZ*>(ptrplan);
    { //清除fft计划的计算缓冲数组输入内容为0
        glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
        glFramebufferTextureLayer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,plan.complexBufs,0,0);
        glFramebufferTextureLayer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,plan.complexBufs,0,1);
        //检查帧缓冲区是否有效
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        }
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);

        GLenum drawdst[2] = {GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};
        glViewport(0, 0, plan.width*2, plan.height);
        glDrawBuffers(2,drawdst);
        glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_FALSE);
        glClearColor(0,0,0,0);
        glClear(GL_COLOR_BUFFER_BIT);
        glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
        glBindFramebuffer(GL_FRAMEBUFFER,0);
    }


    if(srctex>0){   //将输入纹理数据 写入到fft计划的计算缓冲区
        int itype;
        int winmode = 0;
        if(fftmode == GLGP_TexFFTMode_ROW && plan.hasFFTWINBuf[0]) winmode = 1;
        else if(fftmode == GLGP_TexFFTMode_COL && plan.hasFFTWINBuf[1]) winmode = 2;
        else if(fftmode == GLGP_TexFFTMode_ROW_COL && plan.hasFFTWINBuf[0] && plan.hasFFTWINBuf[1]) winmode = 3;
        else if(fftmode == GLGP_TexFFTMode_ROW_COL && plan.hasFFTWINBuf[0] ) winmode = 1;
        else if(fftmode == GLGP_TexFFTMode_ROW_COL && plan.hasFFTWINBuf[1]) winmode = 2;

        //获取输入纹理尺寸和数据类型
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,srctex);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
        glBindTexture(GL_TEXTURE_2D,0);

        glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
//        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,texs[0],0);
        glFramebufferTextureLayer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,plan.complexBufs,0,0); //最后一个参数是数组索引
        //检查帧缓冲区是否有效
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        }
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_Set2DFFTInputTex]);
        if(itype == GL_INT){   //设置源纹理的 sample2D GL_TEXTUREi单元
            glActiveTexture(GL_TEXTURE2);
            glBindTexture(GL_TEXTURE_2D,srctex);
            glUniform1i(22,2);      //输入纹理的类型
        }else if(itype == GL_UNSIGNED_INT){
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D,srctex);
            glUniform1i(22,1);      //输入纹理的类型
        }else{
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D,srctex);
            glUniform1i(22,0);      //输入纹理的类型
        }
        glUniform1i(0,0);       //glsl 中的纹理单元映射
        glUniform1i(1,1);
        glUniform1i(2,2);
        glUniform1i(3,3);
        glUniform1i(4,4);

        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D,plan.fftWINBuf[0]);
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D,plan.fftWINBuf[1]);
        glUniform1i(21,winmode);                          //什么方向乘以窗数据,0:不乘, 1:只乘以列, 2: 只乘以行, 3: 乘以行和乘以列

        glBindImageTexture(0,plan.complexBufs,0,
                           GL_FALSE,0,  //为true的话绑定整个数组，为false指定数组索引
                           GL_READ_WRITE,GL_R32F);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
        GLenum drawdst[1] = {GL_NONE};
        glViewport(0, 0, width, height);
        glDrawBuffers(1,drawdst);
        glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
        glBindVertexArray(ctx->square_vao);
        glDrawArrays(GL_TRIANGLES, 0, 6);   //绘制
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,0);
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
        glUseProgram(0);
    }

    // fft变换
    int bufindex = 0;
    {
        float beginmul = 1.0f;
        GLuint program = 0;

        GLint workGroupSize[3] = {0};

        if(alpha>0) beginmul = alpha;

        if(ctx->programs[_SPI_Custom2DTexFFT]){
            program = ctx->programs[_SPI_Custom2DTexFFT];
        }else{
            program = ctx->programs[_SPI_2DTexFFT];
        }
        glUseProgram(program);
        glGetProgramiv(program, GL_COMPUTE_WORK_GROUP_SIZE, workGroupSize);
        workGroupSize[0] = (plan.width + workGroupSize[0] - 1) / workGroupSize[0];
        workGroupSize[1] = (height + workGroupSize[1] - 1) / workGroupSize[1];

        glBindImageTexture(0,plan.complexBufs,0,GL_TRUE,0,GL_READ_WRITE,GL_R32F);   //绑定整个缓冲区数组

        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,ctx->byteMirrorLut);    //字节镜像查找表纹理
        glUniform1i(0,0);   //设置纹理单元对应的sampler
        glUniform1i(1,1);
        glUniform2ui(23,static_cast<GLuint>(plan.width),static_cast<GLuint>(plan.height));
        if(fftdir == GLGP_TexFFTDir_FFT)
            glUniform1f(24,1.0f);   //控制正反变换，-1.0f为逆变换
        else if(fftdir == GLGP_TexFFTDir_IFFT)
            glUniform1f(24,-1.0f);

        if(fftmode == GLGP_TexFFTMode_ROW || fftmode == GLGP_TexFFTMode_ROW_COL){   //每行进行变换
            if(alpha<0) {
                beginmul = 1.0f / static_cast<float>(plan.width);
                if(fftmode == GLGP_TexFFTMode_ROW_COL){
                    beginmul = beginmul / static_cast<float>(plan.height);
                }
            }
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D,plan.wkBuf[0]); //每行数据乘以的WkN旋转复数

            glUniform1ui(20,0u);    //每行数据进行fft变换,0行数据进行fft,1每列数据进行fft
            glUniform1i(21,plan.colbitcnt);    //每行数据长度可用多少位二进制表示,或者蝶形计算的次数
            glUniform1f(25,beginmul);   //变换前数据乘以的倍率,通常用于逆变换除N

            {   //蝶形运算前先进行数据重排序
                glUniform1i(22,bufindex);  //选择bufindex索引计算缓冲纹理数组作为输入
                glUniform1i(26,-1);  //循环次数为-1表示进行数据重排序
                glDispatchCompute(static_cast<GLuint>(workGroupSize[0]),static_cast<GLuint>(workGroupSize[1]),1);
                glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);    //等待纹理写入完成
                bufindex = 1 - bufindex;    //调换输入输出
            }

            for (int i = 0; i < plan.colbitcnt; ++i) {  //由于glsl barrier()函数不能同步全局调用，所以改为管线外循环
                glUniform1i(22,bufindex);  //选择bufindex索引计算缓冲纹理数组作为输入
                glUniform1i(26,i);  //当前循环次数
                glDispatchCompute(static_cast<GLuint>(workGroupSize[0]),static_cast<GLuint>(workGroupSize[1]),1);
                glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);    //等待纹理写入完成
                bufindex = 1 - bufindex;    //调换输入输出
            }

        }

        if(fftmode == GLGP_TexFFTMode_COL || fftmode == GLGP_TexFFTMode_ROW_COL){   //每列进行变换
            glGetProgramiv(program, GL_COMPUTE_WORK_GROUP_SIZE, workGroupSize);
            workGroupSize[1] = (plan.height + workGroupSize[1] - 1) / workGroupSize[1];
            if(fftmode == GLGP_TexFFTMode_COL){
                bufindex = 0;
                if(alpha<0){
                    beginmul = 1.0f / static_cast<float>(plan.height);
                }
                workGroupSize[0] = (width + workGroupSize[0] - 1) / workGroupSize[0];
            }else if(fftmode == GLGP_TexFFTMode_ROW_COL){
                beginmul = 1.0f;
                workGroupSize[0] = (plan.width + workGroupSize[0] - 1) / workGroupSize[0];
            }

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D,plan.wkBuf[1]); //每行数据乘以的WkN旋转复数

            glUniform1ui(20,1u);    //每行数据进行fft变换,0行数据进行fft,1每列数据进行fft
            glUniform1i(21,plan.rowbitcnt);    //每行数据长度可用多少位二进制表示,或者蝶形计算的次数
            glUniform1f(25,beginmul);   //变换前数据乘以的倍率,通常用于逆变换除N

            {
                glUniform1i(22,bufindex);
                glUniform1i(26,-1);
                glDispatchCompute(static_cast<GLuint>(workGroupSize[0]),static_cast<GLuint>(workGroupSize[1]),1);
                glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
                bufindex = 1 - bufindex;
            }

            for (int i = 0; i < plan.rowbitcnt; ++i) {
                glUniform1i(22,bufindex);
                glUniform1i(26,i);
                glDispatchCompute(static_cast<GLuint>(workGroupSize[0]),static_cast<GLuint>(workGroupSize[1]),1);
                glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);    //等待纹理写入完成
                bufindex = 1 - bufindex;
            }
        }

        glUseProgram(0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,0);
        glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_WRITE,GL_R32F);
    }

    {   //将fft计划的计算缓冲区数据 写入到输出纹理
        int itype;
        //获取输出纹理尺寸和数据类型
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
        glBindTexture(GL_TEXTURE_2D,0);

        if(itype != GL_FLOAT){
            std::cerr << "2D FFT result should be copy to float texture!" << std::endl;
        }

        glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        //检查帧缓冲区是否有效
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        }
        glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_Get2DFFTResTex]);
        glUniform1i(0,0);       //glsl 中的纹理单元映射
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D_ARRAY,plan.complexBufs);
        glUniform1i(20,bufindex);  //指定读取的数组索引
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
        GLenum drawdst[1] = {GL_COLOR_ATTACHMENT0};
        glViewport(0, 0, width, height);
        glDrawBuffers(1,drawdst);
        glColorMask(GL_TRUE,GL_TRUE,GL_FALSE,GL_FALSE);
        glBindVertexArray(ctx->square_vao);
        glDrawArrays(GL_TRIANGLES, 0, 6);   //绘制
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,0);
        glBindTexture(GL_TEXTURE_2D_ARRAY,0);
        glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
        glUseProgram(0);
    }

    glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_ONLY,GL_RGBA32F);
    glBindImageTexture(1,0,0,GL_FALSE,0,GL_READ_ONLY,GL_RGBA32F);

    GLenum err = glGetError();
    if(err){
        std::cerr<<"glgpExecTex2DFFTPlan error! error code = "<< err << std::endl;
    }
    return static_cast<int>(err);
}

void glgpGenFFTWindData(float *dstbuf, int complexlen, int type)
{
    if(!dstbuf || complexlen<2) return;
    const float hammin_a = 0.46f;
    for (int i = 0; i < complexlen; ++i) {
        if(type == GLGP_FFTWin_Hanning){
            dstbuf[i*2] = 0.5f * (1.0f - cosf( 2.0f * M_PI_F * i / (complexlen - 1)) );
            dstbuf[i*2 + 1] = 0;
        }else if(type == GLGP_FFTWind_Hamming){
            dstbuf[i*2] = (1.0f - hammin_a) - hammin_a * cosf( 2.0f * M_PI_F * i / (complexlen - 1));
            dstbuf[i*2 + 1] = 0;
        }
    }
}

int glgpSetTex2DFFTPlanWinData(GLGP_FFTPlan2D ptrplan, int rowORcol, float *srcbuf, int complexlen)
{
    if(ptrplan == nullptr || complexlen < 2) return -1;
    if(rowORcol != GLGP_TexFFTMode_ROW && rowORcol != GLGP_TexFFTMode_COL) return -1;



    GLGP_FFTPlan2D_SZ &plan = *reinterpret_cast<GLGP_FFTPlan2D_SZ*>(ptrplan);
    unsigned int fftwintex = 0;

    if(srcbuf == nullptr){
        if(rowORcol == GLGP_TexFFTMode_ROW){
            plan.hasFFTWINBuf[0] = 0;
        }else if(rowORcol == GLGP_TexFFTMode_COL){
            plan.hasFFTWINBuf[1] = 0;
        }
        return 0;
    }

    if(rowORcol == GLGP_TexFFTMode_ROW){
        fftwintex = plan.fftWINBuf[0];
    }else if(rowORcol == GLGP_TexFFTMode_COL){
        fftwintex = plan.fftWINBuf[1];
    }
    glgpFillTexture(fftwintex,0,0,0,0,0B0011);  //全部清零
    glBindTexture(GL_TEXTURE_2D,fftwintex);
    glTexSubImage2D(GL_TEXTURE_2D,0,
                    0,0,complexlen,1,
                    GL_RG,GL_FLOAT,
                    srcbuf);
    glBindTexture(GL_TEXTURE_2D,0);

    GLenum err = glGetError();
    if(err){
        std::cerr<<"glgpSetTex2DFFTPlanWinData error, FFT plan windows data will not update! error code = "<< err << std::endl;
    }else{
        if(rowORcol == GLGP_TexFFTMode_ROW){
            plan.hasFFTWINBuf[0] = 1;
        }else if(rowORcol == GLGP_TexFFTMode_COL){
            plan.hasFFTWINBuf[1] = 1;
        }
    }

    return static_cast<int>(err);
}

int glgpTexTranspose(unsigned int srctex, unsigned int dsttex)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;

    int width=0,height=0,itype = GL_UNSIGNED_INT,otype = GL_UNSIGNED_INT;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (dsttex == srctex),medtexformat=0;
    GLContextSZ * ctx =  reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    //获取输出纹理尺寸和数据类型
    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
    }

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        otype = 2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
        drawdst[2] = GL_COLOR_ATTACHMENT2;
    }else if(otype == GL_UNSIGNED_INT){
        otype = 1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
        drawdst[1] = GL_COLOR_ATTACHMENT1;
    }else if(otype == GL_FLOAT){
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }else{
        otype = 0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        drawdst[0] = GL_COLOR_ATTACHMENT0;
    }
    glBindTexture(GL_TEXTURE_2D,0);

    glUseProgram(ctx->programs[_ShaderProgramIndex::_SPI_TexTranspose]);

    //获取输入纹理数据类型
    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    glBindTexture(GL_TEXTURE_2D,0);
    if(itype == GL_INT){
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        itype = 1;
    }else if(itype == GL_FLOAT){
        itype = 0;
    }else{
        itype = 0;
    }

    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    glUniform1i(0,0);   //设置源纹理的 sample2D对应GL_TEXTURE0
    glUniform1i(1,1);
    glUniform1i(2,2);
    glUniform2i(21,width,height);
    glUniform1i(22,itype);

    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glBindTexture(GL_TEXTURE_2D,0);

    if(itype==0) glActiveTexture(GL_TEXTURE0);
    if(itype==1) glActiveTexture(GL_TEXTURE1);
    if(itype==2) glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,srctex);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);
    //    glClearColor(0,0,0,0);
    //    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }
    return static_cast<int>(glGetError());

}

int glgpTex2DHist1DCalc(unsigned int srctex, unsigned int dsttex, int bins, float rangestart, float rangeend)
{
    if(glIsTexture(srctex)!=GL_TRUE || glIsTexture(dsttex)!=GL_TRUE) return -1;
    if(srctex == dsttex){
        std::cerr<<"glgpTex2DHist1DCalc srctex and dsttex can not be same!"<<std::endl;
        return -1;
    }
    if(bins<1) return -1;
    if(rangestart>=rangeend) return -1;

    int width, height, numcomp = 0, dbytesize = 0;
    char dt = 'u';
    glgpGetTexture2DInfo(dsttex,&width,&height,&numcomp,&dbytesize,&dt);
    if(dt != 'u' || dbytesize != 4){
        std::cerr << "glgpTex2DHist1DCalc data type of dsttex is not unsigned int!"<<std::endl;
        return -1;
    }
    if(width < bins){
        std::cerr << "glgpTex2DHist1DCalc width of dsttex is less than bins!"<<std::endl;
        return -1;
    }

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );

    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    //结果目标纹理进行清零
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
    glViewport(0, 0, width, 1);
    GLenum outputs[] = {GL_COLOR_ATTACHMENT0};
    glDrawBuffers(1,outputs);
    glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_FALSE);
    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT);   //直方图数据清零

    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,srctex,0);
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
    }

    glUseProgram(ctx->programs[_SPI_Tex2DHist1DCalc]);
    glBindImageTexture(0,dsttex,0,GL_FALSE,0,GL_READ_WRITE,GL_R32UI);

    glUniform3f(21,rangestart,rangeend - rangestart, static_cast<float>(bins));

    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&dbytesize);
    glBindTexture(GL_TEXTURE_2D,0);
    if(dbytesize == GL_FLOAT) {
        glUniform1i(22,2);
        glActiveTexture(GL_TEXTURE2);
    }
    else if(dbytesize == GL_UNSIGNED_INT){
        glUniform1i(22,1);
        glActiveTexture(GL_TEXTURE1);
    }
    else if(dbytesize == GL_INT){
        glUniform1i(22,0);
        glActiveTexture(GL_TEXTURE0);
    }
    glBindTexture(GL_TEXTURE_2D,srctex);
    glUniform1i(0,0);
    glUniform1i(1,1);
    glUniform1i(2,2);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glViewport(0, 0, width, height);
    glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);

    outputs[0] = GL_NONE;
    glDrawBuffers(1,outputs);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_WRITE,GL_R32UI);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glUseProgram(0);

//    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);    //等待纹理写入完成
    return static_cast<int>(glGetError());
}

int glgpTex2DConvolve2D(unsigned int srctex, unsigned int dsttex, unsigned int kerneltex, int padding, int colormask)
{
    if(glIsTexture(srctex) != GL_TRUE ||
            glIsTexture(dsttex) != GL_TRUE ||
            glIsTexture(kerneltex) != GL_TRUE) return -1;

    int width, height,itype,otype;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (dsttex == srctex),medtexformat=0;
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    glBindTexture(GL_TEXTURE_2D,kerneltex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    glBindTexture(GL_TEXTURE_2D,0);

    if(itype != GL_FLOAT){
        std::cerr << "glgpTex2DConvolve2D type of kerneltex is not float!"<<std::endl;
        return -1;
    }

    if(width < 3 || height < 3){
        std::cerr << "glgpTex2DConvolve2D shape of kerneltex is invalid!"<<std::endl;
        return -1;
    }

    if((width % 2) != 1 || (height % 2) != 1 ){
        std::cerr << "glgpTex2DConvolve2D shape of kerneltex is not odd!"<<std::endl;
    }

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    unsigned int program = ctx->programs[_SPI_Tex2DConvolve2D];
    glUseProgram(program);
    glUniform1i(0,0);
    glUniform1i(1,1);
    glUniform1i(2,2);
    glUniform1i(5,5);
    glUniform2i(20,width,height);   //卷积核宽高
    if(padding == 0) glUniform1i(21,0);     //填充零
    else if(padding == 1) glUniform1i(21,1);  //填充边缘值
    else glUniform1i(21,2);     //填充镜像像素值

    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);
    }
    glBindTexture(GL_TEXTURE_2D,0);
    glUniform2i(23,width,height);   //输入纹理的尺寸

    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);


    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
        otype = 2;
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
        otype = 1;
    }else if(otype == GL_FLOAT){
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        otype = 0;
    }
    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        glUseProgram(0);
        glActiveTexture(GL_TEXTURE0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    if(itype == GL_INT){
        glActiveTexture(GL_TEXTURE2);
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);
        itype = 1;
    }else if(itype == GL_FLOAT){
        glActiveTexture(GL_TEXTURE0);
        itype = 0;
    }
    glBindTexture(GL_TEXTURE_2D,srctex);
    glActiveTexture(GL_TEXTURE5);
    glBindTexture(GL_TEXTURE_2D,kerneltex);
    glUniform2i(22,itype,otype);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);

    glActiveTexture(GL_TEXTURE5);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
//        glgpTextureCopy(dsttex,srctex,1,0,0);
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }

    return static_cast<int>(glGetError());
}

int glgpFillTex2DGaussianKernel(unsigned int dsttex, float sigma)
{
    if(glIsTexture(dsttex) != GL_TRUE) return -1;
    int width,height,numcomp,dbytesize;
    char dtype = 0;
    glgpGetTexture2DInfo(dsttex,&width,&height,&numcomp,&dbytesize,&dtype);
    if(dtype != 'f'){
        std::cerr << "glgpFillTex2DGaussianKernel type of dsttex is not float!"<<std::endl;
        return -1;
    }

    if((width % 2) != 1 || (height % 2) != 1 ){
        std::cerr << "glgpFillTex2DGaussianKernel shape of dsttex is not odd!"<<std::endl;
    }

    float *v = new float[static_cast<size_t>(width * height * numcomp) * sizeof (float)];

    float t1,t2,sum = 0;
    int cx = (width + 1)/2 - 1,cy = (height + 1)/2 - 1;
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {   //G(x,y) = {exp( -(x^2+y^2)/(2*PI*sigma^2))} / (2*PI*sigma^2)
            t1 = static_cast<float>(x - cx);
            t2 = static_cast<float>(y - cy);
            t1 = t1*t1 + t2*t2;
            t1 = -t1;
            t2 = 2.0f * sigma * sigma;
            t1 = t1 / t2;
            t1 = expf(t1);
            t1 = t1 / t2;
            t1 = t1 /M_PI_F;
            sum += t1;
            for (int c = 0; c < numcomp; ++c) {
                v[y * width * numcomp + x * numcomp + c] = t1;
            }
        }
    }

    width = width * height * numcomp;
    for (int i = 0; i < width; ++i) {   //归一化
        v[i] = v[i] / sum;
    }

    int res = glgpSetTexImage2D(dsttex,v);
    delete [] v;
    return res;
}

int glgpTex2DSobelConv(unsigned int srctex, unsigned int dsttex, int direction, int padding, int colormask)
{
    if(glIsTexture(srctex) != GL_TRUE ||
            glIsTexture(dsttex) != GL_TRUE) return -1;
    int width, height,itype,otype;
    GLenum drawdst[3] = {GL_NONE};
    int needmedtex = (dsttex == srctex),medtexformat=0;
    GLboolean mask[4] = {GL_TRUE};
    for (int i = 0; i < 4; ++i) {
        mask[i] = !((1 << i) & colormask) ? GL_TRUE : GL_FALSE;
    }

    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    unsigned int program = ctx->programs[_SPI_Tex2DConv2DSobel];
    glUseProgram(program);
    glUniform1i(0,0);
    glUniform1i(1,1);
    glUniform1i(2,2);

    if(padding == 0) glUniform1i(21,0);     //填充零
    else if(padding == 1) glUniform1i(21,1);  //填充边缘值
    else glUniform1i(21,2);     //填充镜像像素值

    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    if(needmedtex){
        glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_INTERNAL_FORMAT,&medtexformat);
        glGenTextures(1,&dsttex);
        glBindTexture(GL_TEXTURE_2D,dsttex);
        glTexStorage2D(GL_TEXTURE_2D,1,static_cast<GLenum>(medtexformat),width,height);
        glgpBlitTexture2D(srctex,dsttex);
    }
    glBindTexture(GL_TEXTURE_2D,0);
    glUniform2i(23,width,height);   //输入纹理的尺寸

    glBindTexture(GL_TEXTURE_2D,dsttex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);


    glBindFramebuffer(GL_FRAMEBUFFER,ctx->fbo);

    if(otype == GL_INT){
        drawdst[2] = GL_COLOR_ATTACHMENT2;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,dsttex,0);
        otype = 2;
    }else if(otype == GL_UNSIGNED_INT){
        drawdst[1] = GL_COLOR_ATTACHMENT1;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,dsttex,0);
        otype = 1;
    }else if(otype == GL_FLOAT){
        drawdst[0] = GL_COLOR_ATTACHMENT0;
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,dsttex,0);
        otype = 0;
    }
    //检查帧缓冲区是否有效
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; //最少要附加一个颜色缓冲附件
        glUseProgram(0);
        glActiveTexture(GL_TEXTURE0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        if(needmedtex){
            glDeleteTextures(1,&dsttex);
        }
        return -1;
    }

    if(itype == GL_INT){
        glActiveTexture(GL_TEXTURE2);
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);
        itype = 1;
    }else if(itype == GL_FLOAT){
        glActiveTexture(GL_TEXTURE0);
        itype = 0;
    }
    glBindTexture(GL_TEXTURE_2D,srctex);
    glUniform2i(22,itype,otype);
    if(direction == 1){         //Y方向sobel,列优先数组
        const float sobelkernel[] = {
            -1.0f,0.0f,1.0f,
            -2.0f,0.0f,2.0f,
            -1.0f,0.0f,1.0f,
        };
        glUniformMatrix3fv(20,1,GL_FALSE,sobelkernel);
    }else{                  //X方向sobel,列优先数组
        const float sobelkernel[] = {
            -1.0f,-2.0f,-1.0f,
            0.0f,0.0f,0.0f,
            1.0f,2.0f,1.0f,
        };
        glUniformMatrix3fv(20,1,GL_FALSE,sobelkernel);
    }

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glViewport(0, 0, width, height);
    glDrawBuffers(3,drawdst);

    glColorMask(mask[3],mask[2],mask[1],mask[0]);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(ctx->square_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindVertexArray(0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,0,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,0,0);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    glUseProgram(0);

    if(needmedtex){
        glgpBlitTexture2D(dsttex,srctex);
        glDeleteTextures(1,&dsttex);
    }
    return static_cast<int>(glGetError());
}

int glgpTex2DCanny(unsigned int srctex, unsigned int dsttex, float lowThresh, float highThresh, int padding)
{
    if(glIsTexture(srctex) != GL_TRUE ||
            glIsTexture(dsttex) != GL_TRUE) return -1;

    if(lowThresh > highThresh){
        std::cerr << "warning: glgpTex2DCanny lowThresh should less equl than highThresh!" << std::endl;
    }

    int width, height,itype,otype;
    unsigned int temptex[2] = {0};
    GLint workGroupSize[3] = {0};
    GLContextSZ * ctx = reinterpret_cast<GLContextSZ *>(
                glfwGetWindowUserPointer(glfwGetCurrentContext())
                );
    unsigned int program = ctx->programs[_SPI_Tex2DCannySobelStep];
    glUseProgram(program);
    glUniform1i(0,0);
    glUniform1i(1,1);
    glUniform1i(2,2);

    if(padding == 0) glUniform1i(21,0);     //填充零
    else if(padding == 1) glUniform1i(21,1);  //填充边缘值
    else glUniform1i(21,2);     //填充镜像像素值

    glBindTexture(GL_TEXTURE_2D,srctex);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&itype);
    glBindTexture(GL_TEXTURE_2D,0);
    glUniform2i(23,width,height);   //输入纹理的尺寸

    temptex[0] = glgpAllocateGLTexture2D(GLF_R32F,width,height,nullptr);
    temptex[1] = glgpAllocateGLTexture2D(GLF_R32F,width,height,nullptr);
    glGetProgramiv(program, GL_COMPUTE_WORK_GROUP_SIZE, workGroupSize);
    workGroupSize[0] = (width + workGroupSize[0] - 1) / workGroupSize[0];
    workGroupSize[1] = (height + workGroupSize[1] - 1) / workGroupSize[1];

    glBindImageTexture(0,temptex[0],0,GL_FALSE,0,GL_READ_WRITE,GL_R32F);   //绑定输出纹理到纹理图像单元0
    glBindImageTexture(1,temptex[1],0,GL_FALSE,0,GL_READ_WRITE,GL_R32F);   //绑定输出纹理到纹理图像单元1

    glBindTexture(GL_TEXTURE_2D,dsttex);
//    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
//    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
    glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_RED_TYPE,&otype);
    glBindTexture(GL_TEXTURE_2D,0);

    if(itype == GL_INT){
        glActiveTexture(GL_TEXTURE2);
        itype = 2;
    }else if(itype == GL_UNSIGNED_INT){
        glActiveTexture(GL_TEXTURE1);
        itype = 1;
    }else if(itype == GL_FLOAT){
        glActiveTexture(GL_TEXTURE0);
        itype = 0;
    }
    glBindTexture(GL_TEXTURE_2D,srctex);
    glUniform2i(22,itype,otype);

    const float sobelkernelx[] = {  //X方向sobel,列优先数组
        -1.0f,-2.0f,-1.0f,
        0.0f,0.0f,0.0f,
        1.0f,2.0f,1.0f,
    };
    const float sobelkernely[] = {  //Y方向sobel,列优先数组
        -1.0f,0.0f,1.0f,
        -2.0f,0.0f,2.0f,
        -1.0f,0.0f,1.0f,
    };
    glUniformMatrix3fv(24,1,GL_FALSE,sobelkernelx);
    glUniformMatrix3fv(25,1,GL_FALSE,sobelkernely);

    glDispatchCompute(static_cast<GLuint>(workGroupSize[0]),
            static_cast<GLuint>(workGroupSize[1]),1);   //计算着色器分配执行

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glBindImageTexture(0,0,0,GL_FALSE,0,GL_READ_WRITE,GL_R32F);
    glBindImageTexture(1,0,0,GL_FALSE,0,GL_READ_WRITE,GL_R32F);
    glUseProgram(0);
    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);    //等待纹理写入完成


    {   //复制计算结果
        int w,h,c,b;
        char t;
        glgpGetTexture2DInfo(dsttex,&w,&h,&c,&b,&t);
        if(w == width && h == height && c == 1 && b == 4 && t == 'f'){
            glgpBlitTexture2D(temptex[0],dsttex);
        }else{
            glgpTextureCopy(temptex[1],dsttex,1,0,0B0111);
        }
    }

    glDeleteTextures(2,temptex);
    return static_cast<int>(glGetError());
}
